diff -r d48faf442569 Lib/hashlib.py --- a/Lib/hashlib.py Wed Aug 14 14:41:48 2013 -0400 +++ b/Lib/hashlib.py Thu Aug 15 00:11:22 2013 +0200 @@ -51,7 +51,7 @@ 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' """ - +import abc as _abc # This tuple and __get_builtin_constructor() must be modified if a new # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', @@ -63,44 +63,85 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed', 'algorithms_available') +class CryptoHash(metaclass=_abc.ABCMeta): + """Abstract base class for cryptographic hash functions (PEP 247)""" + __slots__ = () -def __get_builtin_constructor(name): + def __init__(self, string=None): + # Factories accept a 'string' keyword arguments but it's really bytes. + if string is not None: + self.update(string) + + @_abc.abstractproperty + def digest_size(self): + """The size of the digest produced by the hashing objects.""" + raise NotImplementedError + + @_abc.abstractproperty + def name(self): + """The name of the digest.""" + raise NotImplementedError + + @_abc.abstractmethod + def copy(self): + """Return a separate copy of this hashing object.""" + raise NotImplementedError + + @_abc.abstractmethod + def digest(self): + """Return the hash value as 8-bit data (bytes).""" + raise NotImplementedError + + def hexdigest(self): + """Return the hash value as string containing hexadecimal digits.""" + return ''.join('%x' % b for b in self.digest()) + + @_abc.abstractmethod + def update(self, string): + """Hash 'string' into the current state of the hashing object.""" + raise NotImplementedError + + +def __get_builtin_constructor(name, _cache={}): + constructor = _cache.get(name) + if constructor is not None: + return constructor try: if name in ('SHA1', 'sha1'): import _sha1 - return _sha1.sha1 + CryptoHash.register(_sha1.SHA1Type) + _cache['SHA1'] = _cache['sha1'] = _sha1.sha1 elif name in ('MD5', 'md5'): import _md5 - return _md5.md5 + CryptoHash.register(_md5.MD5Type) + _cache['MD5'] = _cache['md5'] = _md5.md5 elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): import _sha256 - bs = name[3:] - if bs == '256': - return _sha256.sha256 - elif bs == '224': - return _sha256.sha224 + CryptoHash.register(_sha256.SHA224Type) + CryptoHash.register(_sha256.SHA256Type) + _cache['SHA224'] = _cache['sha224'] = _sha256.sha224 + _cache['SHA256'] = _cache['sha256'] = _sha256.sha256 elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): import _sha512 - bs = name[3:] - if bs == '512': - return _sha512.sha512 - elif bs == '384': - return _sha512.sha384 + CryptoHash.register(_sha512.SHA384Type) + CryptoHash.register(_sha512.SHA512Type) + _cache['SHA384'] = _cache['sha384'] = _sha512.sha384 + _cache['SHA512'] = _cache['sha512'] = _sha512.sha512 elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'SHA3_224', 'SHA3_256', 'SHA3_384', 'SHA3_512'}: import _sha3 - bs = name[5:] - if bs == '224': - return _sha3.sha3_224 - elif bs == '256': - return _sha3.sha3_256 - elif bs == '384': - return _sha3.sha3_384 - elif bs == '512': - return _sha3.sha3_512 + CryptoHash.register(_sha3.SHA3Type) + _cache['SHA3_224'] = _cache['sha3_224'] = _sha3.sha3_224 + _cache['SHA3_256'] = _cache['sha3_256'] = _sha3.sha3_256 + _cache['SHA3_384'] = _cache['sha3_384'] = _sha3.sha3_384 + _cache['SHA3_512'] = _cache['sha3_512'] = _sha3.sha3_512 except ImportError: pass # no extension module, this hash is unsupported. + constructor = _cache.get(name) + if constructor is not None: + return constructor + raise ValueError('unsupported hash type ' + name) @@ -139,6 +180,7 @@ try: import _hashlib + CryptoHash.register(_hashlib.HASH) new = __hash_new __get_hash = __get_openssl_constructor algorithms_available = algorithms_available.union( diff -r d48faf442569 Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py Wed Aug 14 14:41:48 2013 -0400 +++ b/Lib/test/test_hashlib.py Thu Aug 15 00:11:22 2013 +0200 @@ -82,26 +82,30 @@ if constructor: constructors.add(constructor) + def add_builtin_constructor(name): + constructor = getattr(hashlib, "__get_builtin_constructor")(name) + self.constructors_to_test[name].add(constructor) + _md5 = self._conditional_import_module('_md5') if _md5: - self.constructors_to_test['md5'].add(_md5.md5) + add_builtin_constructor('md5') _sha1 = self._conditional_import_module('_sha1') if _sha1: - self.constructors_to_test['sha1'].add(_sha1.sha1) + add_builtin_constructor('sha1') _sha256 = self._conditional_import_module('_sha256') if _sha256: - self.constructors_to_test['sha224'].add(_sha256.sha224) - self.constructors_to_test['sha256'].add(_sha256.sha256) + add_builtin_constructor('sha224') + add_builtin_constructor('sha256') _sha512 = self._conditional_import_module('_sha512') if _sha512: - self.constructors_to_test['sha384'].add(_sha512.sha384) - self.constructors_to_test['sha512'].add(_sha512.sha512) + add_builtin_constructor('sha384') + add_builtin_constructor('sha512') _sha3 = self._conditional_import_module('_sha3') if _sha3: - self.constructors_to_test['sha3_224'].add(_sha3.sha3_224) - self.constructors_to_test['sha3_256'].add(_sha3.sha3_256) - self.constructors_to_test['sha3_384'].add(_sha3.sha3_384) - self.constructors_to_test['sha3_512'].add(_sha3.sha3_512) + add_builtin_constructor('sha3_224') + add_builtin_constructor('sha3_256') + add_builtin_constructor('sha3_384') + add_builtin_constructor('sha3_512') super(HashLibTestCase, self).__init__(*args, **kwargs) @@ -139,6 +143,8 @@ pass # This forces an ImportError for "import _md5" statements sys.modules['_md5'] = None + # clear the cache + get_builtin_constructor.__defaults__[0].clear() try: self.assertRaises(ValueError, get_builtin_constructor, 'md5') finally: @@ -249,6 +255,17 @@ self.check_blocksize_name('sha3_384', NotImplemented, 48) self.check_blocksize_name('sha3_512', NotImplemented, 64) + def test_hashlib_abc(self): + with self.assertRaises(TypeError) as cm: + hashlib.CryptoHash() + self.assertEqual(str(cm.exception), "Can't instantiate abstract " + "class CryptoHash with abstract methods copy, " + "digest, digest_size, hexdigest, name, update") + for hash_object_constructor in self.hash_constructors: + m = hash_object_constructor() + self.assertIsInstance(m, hashlib.CryptoHash) + self.assertTrue(issubclass(type(m), hashlib.CryptoHash), type(m)) + def test_case_md5_0(self): self.check('md5', b'', 'd41d8cd98f00b204e9800998ecf8427e') diff -r d48faf442569 Modules/_hashopenssl.c --- a/Modules/_hashopenssl.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/_hashopenssl.c Thu Aug 15 00:11:22 2013 +0200 @@ -645,10 +645,7 @@ return NULL; } -#if HASH_OBJ_CONSTRUCTOR - Py_INCREF(&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); -#endif /* these constants are used by the convenience constructors */ INIT_CONSTRUCTOR_CONSTANTS(md5); diff -r d48faf442569 Modules/_sha3/sha3module.c --- a/Modules/_sha3/sha3module.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/_sha3/sha3module.c Thu Aug 15 00:11:22 2013 +0200 @@ -576,10 +576,17 @@ PyMODINIT_FUNC PyInit__sha3(void) { + PyObject *m; + Py_TYPE(&SHA3type) = &PyType_Type; if (PyType_Ready(&SHA3type) < 0) { return NULL; } - return PyModule_Create(&_SHA3module); + m = PyModule_Create(&_SHA3module); + if (m == NULL) + return NULL; + + PyModule_AddObject(m, "SHA3Type", (PyObject *)&SHA3type); + return m; } diff -r d48faf442569 Modules/md5module.c --- a/Modules/md5module.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/md5module.c Thu Aug 15 00:11:22 2013 +0200 @@ -572,8 +572,16 @@ PyMODINIT_FUNC PyInit__md5(void) { + PyObject *m; + Py_TYPE(&MD5type) = &PyType_Type; if (PyType_Ready(&MD5type) < 0) return NULL; - return PyModule_Create(&_md5module); + + m = PyModule_Create(&_md5module); + if (m == NULL) + return NULL; + + PyModule_AddObject(m, "MD5Type", (PyObject *)&MD5type); + return m; } diff -r d48faf442569 Modules/sha1module.c --- a/Modules/sha1module.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/sha1module.c Thu Aug 15 00:11:22 2013 +0200 @@ -544,8 +544,16 @@ PyMODINIT_FUNC PyInit__sha1(void) { + PyObject *m; + Py_TYPE(&SHA1type) = &PyType_Type; if (PyType_Ready(&SHA1type) < 0) return NULL; - return PyModule_Create(&_sha1module); + + m = PyModule_Create(&_sha1module); + if (m == NULL) + return NULL; + + PyModule_AddObject(m, "SHA1Type", (PyObject *)&SHA1type); + return m; } diff -r d48faf442569 Modules/sha256module.c --- a/Modules/sha256module.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/sha256module.c Thu Aug 15 00:11:22 2013 +0200 @@ -706,11 +706,21 @@ PyMODINIT_FUNC PyInit__sha256(void) { + PyObject *m; + Py_TYPE(&SHA224type) = &PyType_Type; if (PyType_Ready(&SHA224type) < 0) return NULL; Py_TYPE(&SHA256type) = &PyType_Type; if (PyType_Ready(&SHA256type) < 0) return NULL; - return PyModule_Create(&_sha256module); + + m = PyModule_Create(&_sha256module); + if (m == NULL) + return NULL; + + PyModule_AddObject(m, "SHA224Type", (PyObject *)&SHA224type); + PyModule_AddObject(m, "SHA256Type", (PyObject *)&SHA256type); + return m; + } diff -r d48faf442569 Modules/sha512module.c --- a/Modules/sha512module.c Wed Aug 14 14:41:48 2013 -0400 +++ b/Modules/sha512module.c Thu Aug 15 00:11:22 2013 +0200 @@ -772,13 +772,22 @@ PyMODINIT_FUNC PyInit__sha512(void) { + PyObject *m; + Py_TYPE(&SHA384type) = &PyType_Type; if (PyType_Ready(&SHA384type) < 0) return NULL; Py_TYPE(&SHA512type) = &PyType_Type; if (PyType_Ready(&SHA512type) < 0) return NULL; - return PyModule_Create(&_sha512module); + + m = PyModule_Create(&_sha512module); + if (m == NULL) + return NULL; + + PyModule_AddObject(m, "SHA384Type", (PyObject *)&SHA384type); + PyModule_AddObject(m, "SHA512Type", (PyObject *)&SHA512type); + return m; } #endif