| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | # -*- coding: utf-8 -*- | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2009-02-28 13:24:04 -05:00
										 |  |  | # Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net> | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2009-02-28 13:24:04 -05:00
										 |  |  | # =================================================================== | 
					
						
							|  |  |  | # The contents of this file are dedicated to the public domain.  To | 
					
						
							|  |  |  | # the extent that dedication to the public domain is not available, | 
					
						
							|  |  |  | # everyone is granted a worldwide, perpetual, royalty-free, | 
					
						
							|  |  |  | # non-exclusive license to exercise all rights associated with the | 
					
						
							|  |  |  | # contents of this file for any purpose whatsoever. | 
					
						
							|  |  |  | # No rights are reserved. | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2009-02-28 13:24:04 -05:00
										 |  |  | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
					
						
							|  |  |  | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
					
						
							|  |  |  | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
					
						
							|  |  |  | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
					
						
							|  |  |  | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
					
						
							|  |  |  | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
					
						
							|  |  |  | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
					
						
							|  |  |  | # SOFTWARE. | 
					
						
							|  |  |  | # =================================================================== | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | """Self-testing for PyCrypto hash modules""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | import binascii | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:35 -08:00
										 |  |  | import Crypto.Hash | 
					
						
							| 
									
										
										
										
											2010-12-28 16:26:52 -05:00
										 |  |  | from Crypto.Util.py3compat import * | 
					
						
							| 
									
										
										
										
											2013-05-14 19:00:43 +02:00
										 |  |  | from Crypto.Util.strxor import strxor_c | 
					
						
							| 
									
										
										
										
											2008-09-14 15:20:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-16 22:41:21 +02:00
										 |  |  | class HashDigestSizeSelfTest(unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2014-05-05 21:31:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-16 22:41:21 +02:00
										 |  |  |     def __init__(self, hashmod, description, expected): | 
					
						
							|  |  |  |         unittest.TestCase.__init__(self) | 
					
						
							|  |  |  |         self.hashmod = hashmod | 
					
						
							|  |  |  |         self.expected = expected | 
					
						
							|  |  |  |         self.description = description | 
					
						
							| 
									
										
										
										
											2014-05-05 21:31:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-16 22:41:21 +02:00
										 |  |  |     def shortDescription(self): | 
					
						
							|  |  |  |         return self.description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							|  |  |  |         self.failUnless(hasattr(self.hashmod, "digest_size")) | 
					
						
							|  |  |  |         self.assertEquals(self.hashmod.digest_size, self.expected) | 
					
						
							|  |  |  |         h = self.hashmod.new() | 
					
						
							|  |  |  |         self.failUnless(hasattr(h, "digest_size")) | 
					
						
							|  |  |  |         self.assertEquals(h.digest_size, self.expected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | class HashSelfTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, hashmod, description, expected, input): | 
					
						
							|  |  |  |         unittest.TestCase.__init__(self) | 
					
						
							|  |  |  |         self.hashmod = hashmod | 
					
						
							|  |  |  |         self.expected = expected | 
					
						
							|  |  |  |         self.input = input | 
					
						
							|  |  |  |         self.description = description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def shortDescription(self): | 
					
						
							|  |  |  |         return self.description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							|  |  |  |         h = self.hashmod.new() | 
					
						
							|  |  |  |         h.update(self.input) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         out1 = binascii.b2a_hex(h.digest()) | 
					
						
							|  |  |  |         out2 = h.hexdigest() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         h = self.hashmod.new(self.input) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         out3 = h.hexdigest() | 
					
						
							|  |  |  |         out4 = binascii.b2a_hex(h.digest()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-05 21:31:19 +02:00
										 |  |  |         # PY3K: hexdigest() should return str(), and digest() bytes | 
					
						
							| 
									
										
										
										
											2008-11-23 16:37:17 -05:00
										 |  |  |         self.assertEqual(self.expected, out1)   # h = .new(); h.update(data); h.digest() | 
					
						
							| 
									
										
										
										
											2010-12-29 13:21:05 -05:00
										 |  |  |         if sys.version_info[0] == 2: | 
					
						
							| 
									
										
										
										
											2010-12-28 16:26:52 -05:00
										 |  |  |             self.assertEqual(self.expected, out2)   # h = .new(); h.update(data); h.hexdigest() | 
					
						
							|  |  |  |             self.assertEqual(self.expected, out3)   # h = .new(data); h.hexdigest() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.assertEqual(self.expected.decode(), out2)   # h = .new(); h.update(data); h.hexdigest() | 
					
						
							|  |  |  |             self.assertEqual(self.expected.decode(), out3)   # h = .new(data); h.hexdigest() | 
					
						
							| 
									
										
										
										
											2008-11-23 16:37:17 -05:00
										 |  |  |         self.assertEqual(self.expected, out4)   # h = .new(data); h.digest() | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:38 -08:00
										 |  |  |         # Verify that the .new() method produces a fresh hash object, except | 
					
						
							|  |  |  |         # for MD5 and SHA1, which are hashlib objects.  (But test any .new() | 
					
						
							|  |  |  |         # method that does exist.) | 
					
						
							|  |  |  |         if self.hashmod.__name__ not in ('Crypto.Hash.MD5', 'Crypto.Hash.SHA1') or hasattr(h, 'new'): | 
					
						
							|  |  |  |             h2 = h.new() | 
					
						
							|  |  |  |             h2.update(self.input) | 
					
						
							|  |  |  |             out5 = binascii.b2a_hex(h2.digest()) | 
					
						
							|  |  |  |             self.assertEqual(self.expected, out5) | 
					
						
							| 
									
										
										
										
											2011-02-07 22:32:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:35 -08:00
										 |  |  |         # Verify that Crypto.Hash.new(h) produces a fresh hash object | 
					
						
							|  |  |  |         h3 = Crypto.Hash.new(h) | 
					
						
							|  |  |  |         h3.update(self.input) | 
					
						
							|  |  |  |         out6 = binascii.b2a_hex(h3.digest()) | 
					
						
							|  |  |  |         self.assertEqual(self.expected, out6) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if hasattr(h, 'name'): | 
					
						
							|  |  |  |             # Verify that Crypto.Hash.new(h.name) produces a fresh hash object | 
					
						
							|  |  |  |             h4 = Crypto.Hash.new(h.name) | 
					
						
							|  |  |  |             h4.update(self.input) | 
					
						
							|  |  |  |             out7 = binascii.b2a_hex(h4.digest()) | 
					
						
							|  |  |  |             self.assertEqual(self.expected, out7) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-03 01:22:13 +01:00
										 |  |  | class HashTestOID(unittest.TestCase): | 
					
						
							|  |  |  |     def __init__(self, hashmod, oid): | 
					
						
							|  |  |  |         unittest.TestCase.__init__(self) | 
					
						
							|  |  |  |         self.hashmod = hashmod | 
					
						
							|  |  |  |         self.oid = oid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							| 
									
										
										
											
												Hash: Remove "oid" attributes; add "name" attribute
In PyCrypto v2.5, the "oid" attribute was added to hash objects.  In
retrospect, this was not a good idea, since the OID is not really a
property of the hash algorithm, it's a protocol-specific identifer for
the hash functions.  PKCS#1 v1.5 uses it, but other protocols (e.g.
OpenPGP, DNSSEC, SSH, etc.) use different identifiers, and it doesn't make
sense to add these to Crypto.Hash.* every time a new algorithm is added.
This also has the benefit of being compatible with the Python standard
library's "hashlib" objects, which also have a name attribute.
											
										 
											2013-02-17 11:21:28 -08:00
										 |  |  |         from Crypto.Signature import PKCS1_v1_5 | 
					
						
							| 
									
										
										
										
											2011-02-03 01:22:13 +01:00
										 |  |  |         h = self.hashmod.new() | 
					
						
							| 
									
										
										
											
												Hash: Remove "oid" attributes; add "name" attribute
In PyCrypto v2.5, the "oid" attribute was added to hash objects.  In
retrospect, this was not a good idea, since the OID is not really a
property of the hash algorithm, it's a protocol-specific identifer for
the hash functions.  PKCS#1 v1.5 uses it, but other protocols (e.g.
OpenPGP, DNSSEC, SSH, etc.) use different identifiers, and it doesn't make
sense to add these to Crypto.Hash.* every time a new algorithm is added.
This also has the benefit of being compatible with the Python standard
library's "hashlib" objects, which also have a name attribute.
											
										 
											2013-02-17 11:21:28 -08:00
										 |  |  |         self.assertEqual(PKCS1_v1_5._HASH_OIDS[h.name], self.oid) | 
					
						
							| 
									
										
										
										
											2011-02-03 01:22:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:38 -08:00
										 |  |  | class HashDocStringTest(unittest.TestCase): | 
					
						
							|  |  |  |     def __init__(self, hashmod): | 
					
						
							|  |  |  |         unittest.TestCase.__init__(self) | 
					
						
							|  |  |  |         self.hashmod = hashmod | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							|  |  |  |         docstring = self.hashmod.__doc__ | 
					
						
							|  |  |  |         self.assert_(hasattr(self.hashmod, '__doc__')) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod.__doc__, str)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:35 -08:00
										 |  |  | class GenericHashConstructorTest(unittest.TestCase): | 
					
						
							|  |  |  |     def __init__(self, hashmod): | 
					
						
							|  |  |  |         unittest.TestCase.__init__(self) | 
					
						
							|  |  |  |         self.hashmod = hashmod | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							|  |  |  |         obj1 = self.hashmod.new("foo") | 
					
						
							|  |  |  |         obj2 = self.hashmod.new() | 
					
						
							|  |  |  |         obj3 = Crypto.Hash.new(obj1.name, "foo") | 
					
						
							|  |  |  |         obj4 = Crypto.Hash.new(obj1.name) | 
					
						
							|  |  |  |         obj5 = Crypto.Hash.new(obj1, "foo") | 
					
						
							|  |  |  |         obj6 = Crypto.Hash.new(obj1) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj1)) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj2)) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj3)) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj4)) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj5)) | 
					
						
							|  |  |  |         self.assert_(isinstance(self.hashmod, obj6)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  | class MACSelfTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |     def __init__(self, module, description, result, input, key, params): | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  |         unittest.TestCase.__init__(self) | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         self.module = module | 
					
						
							|  |  |  |         self.result = result | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  |         self.input = input | 
					
						
							|  |  |  |         self.key = key | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         self.params = params | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  |         self.description = description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def shortDescription(self): | 
					
						
							|  |  |  |         return self.description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def runTest(self): | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         key = binascii.a2b_hex(b(self.key)) | 
					
						
							|  |  |  |         data = binascii.a2b_hex(b(self.input)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Strip whitespace from the expected string (which should be in lowercase-hex) | 
					
						
							|  |  |  |         expected = b("".join(self.result.split())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         h = self.module.new(key, **self.params) | 
					
						
							|  |  |  |         h.update(data) | 
					
						
							|  |  |  |         out1_bin = h.digest() | 
					
						
							|  |  |  |         out1 = binascii.b2a_hex(h.digest()) | 
					
						
							|  |  |  |         out2 = h.hexdigest() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Verify that correct MAC does not raise any exception | 
					
						
							|  |  |  |         h.hexverify(out1) | 
					
						
							|  |  |  |         h.verify(out1_bin) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Verify that incorrect MAC does raise ValueError exception | 
					
						
							|  |  |  |         wrong_mac = strxor_c(out1_bin, 255) | 
					
						
							|  |  |  |         self.assertRaises(ValueError, h.verify, wrong_mac) | 
					
						
							|  |  |  |         self.assertRaises(ValueError, h.hexverify, "4556") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         h = self.module.new(key, data, **self.params) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         out3 = h.hexdigest() | 
					
						
							|  |  |  |         out4 = binascii.b2a_hex(h.digest()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Test .copy() | 
					
						
							|  |  |  |         h2 = h.copy() | 
					
						
							|  |  |  |         h.update(b("blah blah blah"))  # Corrupt the original hash object | 
					
						
							|  |  |  |         out5 = binascii.b2a_hex(h2.digest())    # The copied hash object should return the correct result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-29 17:41:13 -07:00
										 |  |  |         # PY3K: Check that hexdigest() returns str and digest() returns bytes | 
					
						
							|  |  |  |         if sys.version_info[0] > 2: | 
					
						
							|  |  |  |             self.assertTrue(isinstance(h.digest(), type(b("")))) | 
					
						
							|  |  |  |             self.assertTrue(isinstance(h.hexdigest(), type(""))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # PY3K: Check that .hexverify() accepts bytes or str | 
					
						
							|  |  |  |         if sys.version_info[0] > 2: | 
					
						
							|  |  |  |             h.hexverify(h.hexdigest()) | 
					
						
							|  |  |  |             h.hexverify(h.hexdigest().encode('ascii')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # PY3K: hexdigest() should return str, and digest() should return bytes | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         self.assertEqual(expected, out1) | 
					
						
							|  |  |  |         if sys.version_info[0] == 2: | 
					
						
							|  |  |  |             self.assertEqual(expected, out2) | 
					
						
							|  |  |  |             self.assertEqual(expected, out3) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.assertEqual(expected.decode(), out2) | 
					
						
							|  |  |  |             self.assertEqual(expected.decode(), out3) | 
					
						
							|  |  |  |         self.assertEqual(expected, out4) | 
					
						
							|  |  |  |         self.assertEqual(expected, out5) | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-11 12:42:06 +02:00
										 |  |  | def make_hash_tests(module, module_name, test_data, digest_size, oid=None): | 
					
						
							| 
									
										
										
										
											2008-09-17 13:12:24 -04:00
										 |  |  |     tests = [] | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  |     for i in range(len(test_data)): | 
					
						
							|  |  |  |         row = test_data[i] | 
					
						
							| 
									
										
										
										
											2011-10-18 23:20:26 +02:00
										 |  |  |         (expected, input) = map(b,row[0:2]) | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  |         if len(row) < 3: | 
					
						
							|  |  |  |             description = repr(input) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-06-29 12:09:57 +02:00
										 |  |  |             description = row[2] | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  |         name = "%s #%d: %s" % (module_name, i+1, description) | 
					
						
							| 
									
										
										
										
											2008-09-17 13:12:24 -04:00
										 |  |  |         tests.append(HashSelfTest(module, name, expected, input)) | 
					
						
							| 
									
										
										
										
											2011-10-16 22:41:21 +02:00
										 |  |  |     name = "%s #%d: digest_size" % (module_name, i+1) | 
					
						
							|  |  |  |     tests.append(HashDigestSizeSelfTest(module, name, digest_size)) | 
					
						
							| 
									
										
										
										
											2013-04-26 19:23:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Hash: Remove "oid" attributes; add "name" attribute
In PyCrypto v2.5, the "oid" attribute was added to hash objects.  In
retrospect, this was not a good idea, since the OID is not really a
property of the hash algorithm, it's a protocol-specific identifer for
the hash functions.  PKCS#1 v1.5 uses it, but other protocols (e.g.
OpenPGP, DNSSEC, SSH, etc.) use different identifiers, and it doesn't make
sense to add these to Crypto.Hash.* every time a new algorithm is added.
This also has the benefit of being compatible with the Python standard
library's "hashlib" objects, which also have a name attribute.
											
										 
											2013-02-17 11:21:28 -08:00
										 |  |  |     if oid is not None: | 
					
						
							| 
									
										
										
										
											2013-06-15 23:25:49 +02:00
										 |  |  |         tests.append(HashTestOID(module, oid)) | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:38 -08:00
										 |  |  |     tests.append(HashDocStringTest(module)) | 
					
						
							| 
									
										
										
										
											2014-05-05 21:31:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-17 11:21:35 -08:00
										 |  |  |     if getattr(module, 'name', None) is not None: | 
					
						
							|  |  |  |         tests.append(GenericHashConstructorTest(module)) | 
					
						
							| 
									
										
										
										
											2014-05-05 21:31:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-17 13:12:24 -04:00
										 |  |  |     return tests | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  | def make_mac_tests(module, module_name, test_data): | 
					
						
							| 
									
										
										
										
											2008-09-17 13:12:24 -04:00
										 |  |  |     tests = [] | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  |     for i in range(len(test_data)): | 
					
						
							|  |  |  |         row = test_data[i] | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         (key, data, results, description, params) = row | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  |         name = "%s #%d: %s" % (module_name, i+1, description) | 
					
						
							| 
									
										
										
										
											2013-05-16 23:09:27 +02:00
										 |  |  |         tests.append(MACSelfTest(module, name, results, data, key, params)) | 
					
						
							| 
									
										
										
										
											2008-09-17 13:12:24 -04:00
										 |  |  |     return tests | 
					
						
							| 
									
										
										
										
											2008-09-14 14:40:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-12 15:26:24 -04:00
										 |  |  | # vim:set ts=4 sw=4 sts=4 expandtab: |