From 2f8a86bd83138bfef618e763fb8b2147f1214af8 Mon Sep 17 00:00:00 2001 From: Legrandin Date: Sat, 5 May 2012 23:37:19 +0200 Subject: Added documentation for all hash algorithms (including for HMAC which, strictly speaking, does not belong with them). --- lib/Crypto/Hash/HMAC.py | 137 +++++++++++++++++++++++++++++++++----------- lib/Crypto/Hash/MD2.py | 80 ++++++++++++++++++++------ lib/Crypto/Hash/MD4.py | 81 ++++++++++++++++++++------ lib/Crypto/Hash/MD5.py | 84 ++++++++++++++++++++------- lib/Crypto/Hash/RIPEMD.py | 84 ++++++++++++++++++++------- lib/Crypto/Hash/SHA.py | 86 ++++++++++++++++++++------- lib/Crypto/Hash/SHA224.py | 85 +++++++++++++++++++-------- lib/Crypto/Hash/SHA256.py | 82 +++++++++++++++++++------- lib/Crypto/Hash/SHA384.py | 85 +++++++++++++++++++-------- lib/Crypto/Hash/SHA512.py | 84 +++++++++++++++++++-------- lib/Crypto/Hash/__init__.py | 52 ++++++++--------- lib/Crypto/Hash/hashalgo.py | 116 +++++++++++++++++++++++++++++++++++++ lib/Crypto/Util/wrapper.py | 47 --------------- 13 files changed, 801 insertions(+), 302 deletions(-) create mode 100644 lib/Crypto/Hash/hashalgo.py delete mode 100644 lib/Crypto/Util/wrapper.py diff --git a/lib/Crypto/Hash/HMAC.py b/lib/Crypto/Hash/HMAC.py index b09cb36..6244db4 100644 --- a/lib/Crypto/Hash/HMAC.py +++ b/lib/Crypto/Hash/HMAC.py @@ -31,37 +31,71 @@ # =================================================================== -"""HMAC (Keyed-Hashing for Message Authentication) Python module. +"""HMAC (Hash-based Message Authentication Code) algorithm -Implements the HMAC algorithm as described by RFC 2104. +HMAC is a MAC defined in RFC2104_ and FIPS-198_ and constructed using +a cryptograpic hash algorithm. +It is usually named *HMAC-X*, where *X* is the hash algorithm; for +instance *HMAC-SHA1* or *HMAC-MD5*. -This is just a copy of the Python 2.2 HMAC module, modified to work when -used on versions of Python before 2.2. +The strength of an HMAC depends on: + + - the strength of the hash algorithm + - the length and entropy of the secret key + +An example of possible usage is the following: + + >>> from Crypto.Hash import HMAC + >>> + >>> secret = b'Swordfish' + >>> h = HMAC.new(secret) + >>> h.update(b'Hello') + >>> print h.hexdigest() + +.. _RFC2104: http://www.ietf.org/rfc/rfc2104.txt +.. _FIPS-198: http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf """ +# This is just a copy of the Python 2.2 HMAC module, modified to work when +# used on versions of Python before 2.2. + __revision__ = "$Id$" -__all__ = ['new', 'digest_size'] +__all__ = ['new', 'digest_size', 'HMAC' ] from Crypto.Util.strxor import strxor_c from Crypto.Util.py3compat import * -# The size of the digests returned by HMAC depends on the underlying -# hashing module used. +#: The size of the authentication tag produced by the MAC. +#: It matches the digest size on the underlying +#: hashing module used. digest_size = None class HMAC: - """RFC2104 HMAC class. - - This supports the API for Cryptographic Hash Functions (PEP 247). - """ + """Class that implements HMAC""" + #: The size of the authentication tag produced by the MAC. + #: It matches the digest size on the underlying + #: hashing module used. + digest_size = None + def __init__(self, key, msg = None, digestmod = None): """Create a new HMAC object. - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. Defaults to the md5 module. + :Parameters: + key : byte string + secret key for the MAC object. + It must be long enough to match the expected security level of the + MAC. However, there is no benefit in using keys longer than the + `digest_size` of the underlying hash algorithm. + msg : byte string + The very first chunk of the message to authenticate. + It is equivalent to an early call to `update()`. Optional. + :Parameter digestmod: + The hash algorithm the HMAC is based on. + Default is `Crypto.Hash.MD5`. + :Type digestmod: + A hash module or object instantiated from `Crypto.Hash` """ if digestmod is None: import MD5 @@ -94,18 +128,34 @@ class HMAC: if (msg): self.update(msg) -## def clear(self): -## raise NotImplementedError, "clear() method not available in HMAC." - def update(self, msg): - """Update this hashing object with the string msg. + """Continue authentication of a message by consuming the next chunk of data. + + Repeated calls are equivalent to a single call with the concatenation + of all the arguments. In other words: + + >>> m.update(a); m.update(b) + + is equivalent to: + + >>> m.update(a+b) + + :Parameters: + msg : byte string + The next chunk of the message being authenticated """ + self.inner.update(msg) def copy(self): - """Return a separate copy of this hashing object. + """Return a copy ("clone") of the MAC object. + + The copy will have the same internal state as the original MAC + object. + This can be used to efficiently compute the MAC of strings that + share a common initial substring. - An update to this copy won't affect the original object. + :Returns: An `HMAC` object """ other = HMAC(b("")) other.digestmod = self.digestmod @@ -114,32 +164,49 @@ class HMAC: return other def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. The object is - not altered in any way by this function; you can continue - updating the object after calling this function. + """Return the **binary** (non-printable) MAC of the message that has + been authenticated so far. + + This method does not change the state of the MAC object. + You can continue updating the object after calling this function. + + :Return: A byte string of `digest_size` bytes. It may contain non-ASCII + characters, including null bytes. """ h = self.outer.copy() h.update(self.inner.digest()) return h.digest() def hexdigest(self): - """Like digest(), but returns a string of hexadecimal digits instead. + """Return the **printable** MAC of the message that has been + authenticated so far. + + This method does not change the state of the MAC object. + + :Return: A string of 2* `digest_size` bytes. It contains only + hexadecimal ASCII digits. """ return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) def new(key, msg = None, digestmod = None): - """Create a new hashing object and return it. - - key: The starting key for the hash. - msg: if available, will immediately be hashed into the object's starting - state. - - You can now feed arbitrary strings into the object using its update() - method, and can ask for the hash value at any time by calling its digest() - method. + """Create a new HMAC object. + + :Parameters: + key : byte string + key for the MAC object. + It must be long enough to match the expected security level of the + MAC. However, there is no benefit in using keys longer than the + `digest_size` of the underlying hash algorithm. + msg : byte string + The very first chunk of the message to authenticate. + It is equivalent to an early call to `HMAC.update()`. + Optional. + :Parameter digestmod: + The hash to use to implement the HMAC. Default is `Crypto.Hash.MD5`. + :Type digestmod: + A hash module or instantiated object from `Crypto.Hash` + :Returns: An `HMAC` object """ return HMAC(key, msg, digestmod) diff --git a/lib/Crypto/Hash/MD2.py b/lib/Crypto/Hash/MD2.py index 953f763..dac959e 100644 --- a/lib/Crypto/Hash/MD2.py +++ b/lib/Crypto/Hash/MD2.py @@ -18,32 +18,74 @@ # SOFTWARE. # =================================================================== -__revision__ = "$Id$" +"""MD2 cryptographic hash algorithm. -__all__ = ['new', 'digest_size'] +MD2 is specified in RFC1319_ and it produces the 128 bit digest of a message. -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * + >>> from Crypto.Hash import MD2 + >>> + >>> h = MD2.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -# The OID for MD2 is: -# -# id-md2 OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) rsadsi(113549) -# digestAlgorithm(2) 2 -# } +MD2 stand for Message Digest version 2, and it was invented by Rivest in 1989. + +This algorithm is both slow and insecure. Do not use it for new designs. + +.. _RFC1319: http://tools.ietf.org/html/rfc1319 +""" -oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02') +_revision__ = "$Id$" -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +__all__ = ['new', 'digest_size', 'MD2Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo import Crypto.Hash._MD2 as _MD2 hashFactory = _MD2 -digest_size = 16 +class MD2Hash(HashAlgo): + """Class that implements an MD2 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 2 + #: } + #: + #: This value uniquely identifies the MD2 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02') + + digest_size = 16 + block_size = 16 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD2Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD2Hash.update()`. + Optional. + + :Return: An `MD2Hash` object + """ + return MD2Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD2Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD2Hash.block_size diff --git a/lib/Crypto/Hash/MD4.py b/lib/Crypto/Hash/MD4.py index 8a9f595..e28a201 100644 --- a/lib/Crypto/Hash/MD4.py +++ b/lib/Crypto/Hash/MD4.py @@ -18,31 +18,74 @@ # SOFTWARE. # =================================================================== -__revision__ = "$Id$" +"""MD4 cryptographic hash algorithm. -__all__ = ['new', 'digest_size'] +MD4 is specified in RFC1320_ and produces the 128 bit digest of a message. -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * + >>> from Crypto.Hash import MD4 + >>> + >>> h = MD4.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -# The OID for MD4 is: -# -# id-md2 OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) rsadsi(113549) -# digestAlgorithm(2) 4 -# } +MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990. + +This algorithm is insecure. Do not use it for new designs. + +.. _RFC1320: http://tools.ietf.org/html/rfc1320 +""" -oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04') +_revision__ = "$Id$" -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +__all__ = ['new', 'digest_size', 'MD4Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo import Crypto.Hash._MD4 as _MD4 hashFactory = _MD4 -digest_size = 16 +class MD4Hash(HashAlgo): + """Class that implements an MD4 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 4 + #: } + #: + #: This value uniquely identifies the MD4 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD4Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD4Hash.update()`. + Optional. + + :Return: A `MD4Hash` object + """ + return MD4Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD4Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD4Hash.block_size + diff --git a/lib/Crypto/Hash/MD5.py b/lib/Crypto/Hash/MD5.py index 91e5da7..e77c787 100644 --- a/lib/Crypto/Hash/MD5.py +++ b/lib/Crypto/Hash/MD5.py @@ -18,30 +18,29 @@ # SOFTWARE. # =================================================================== -# Just use the MD5 module from the Python standard library +"""MD5 cryptographic hash algorithm. -__revision__ = "$Id$" +MD5 is specified in RFC1321_ and produces the 128 bit digest of a message. -__all__ = ['new', 'digest_size'] + >>> from Crypto.Hash import MD5 + >>> + >>> h = MD5.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * +MD4 stand for Message Digest version 5, and it was invented by Rivest in 1991. -# The OID for MD5 is: -# -# id-md5 OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) rsadsi(113549) -# digestAlgorithm(2) 5 -# } -oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05') - -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +This algorithm is insecure. Do not use it for new designs. + +.. _RFC1321: http://tools.ietf.org/html/rfc1321 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD5Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: # The md5 module is deprecated in Python 2.6, so use hashlib when possible. @@ -52,4 +51,47 @@ except ImportError: import md5 hashFactory = md5 -digest_size = 16 +class MD5Hash(HashAlgo): + """Class that implements an MD5 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md5 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 5 + #: } + #: + #: This value uniquely identifies the MD5 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD5Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD5Hash.update()`. + Optional. + + :Return: A `MD5Hash` object + """ + return MD5Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD5Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD5Hash.block_size + diff --git a/lib/Crypto/Hash/RIPEMD.py b/lib/Crypto/Hash/RIPEMD.py index 4a6c7bf..33099cb 100644 --- a/lib/Crypto/Hash/RIPEMD.py +++ b/lib/Crypto/Hash/RIPEMD.py @@ -18,33 +18,77 @@ # SOFTWARE. # =================================================================== -__revision__ = "$Id$" +"""RIPEMD-160 cryptographic hash algorithm. -__all__ = ['new', 'digest_size'] +RIPEMD-160_ produces the 160 bit digest of a message. -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * + >>> from Crypto.Hash import RIPEMD + >>> + >>> h = RIPEMD.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -# -# See http://homes.esat.kuleuven.be/~bosselae/ripemd160.html#More -# -# id-ripemd160 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) teletrust(36) -# algorithm(3) hashAlgorithm(2) ripemd160(1) -# } +RIPEMD-160 stands for RACE Integrity Primitives Evaluation Message Digest +with a 160 bit digest. It was invented by Dobbertin, Bosselaers, and Preneel. + +This algorithm is considered secure, although it has not been scrutinized as +extensively as SHA-1. Moreover, it provides an informal security level of just +80bits. -oid = b("\x06\x05\x2b\x24\x03\x02\x01") +.. _RIPEMD-160: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html +""" -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'RIPEMD160Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo import Crypto.Hash._RIPEMD160 as _RIPEMD160 hashFactory = _RIPEMD160 -digest_size = 20 +class RIPEMD160Hash(HashAlgo): + """Class that implements a RIPMD-160 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-ripemd160 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) teletrust(36) + #: algorithm(3) hashAlgorithm(2) ripemd160(1) + #: } + #: + #: This value uniquely identifies the RIPMD-160 algorithm. + oid = b("\x06\x05\x2b\x24\x03\x02\x01") + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return RIPEMD160Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `RIPEMD160Hash.update()`. + Optional. + + :Return: A `RIPEMD160Hash` object + """ + return RIPEMD160Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = RIPEMD160Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = RIPEMD160Hash.block_size diff --git a/lib/Crypto/Hash/SHA.py b/lib/Crypto/Hash/SHA.py index e9cd118..0bc5917 100644 --- a/lib/Crypto/Hash/SHA.py +++ b/lib/Crypto/Hash/SHA.py @@ -18,30 +18,29 @@ # SOFTWARE. # =================================================================== -# Just use the SHA module from the Python standard library +"""SHA-1 cryptographic hash algorithm. -__revision__ = "$Id$" +SHA-1_ produces the 160 bit digest of a message. -__all__ = ['new', 'digest_size'] + >>> from Crypto.Hash import SHA + >>> + >>> h = SHA.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -from Crypto.Util.py3compat import * -from Crypto.Util.wrapper import Wrapper +*SHA* stands for Secure Hash Algorithm. -# The OID for SHA-1 is: -# -# id-sha1 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) oiw(14) secsig(3) -# algorithms(2) 26 -# } -oid = b('\x06\x05\x2b\x0e\x03\x02\x1a') - -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +This algorithm is not considered secure. Do not use it for new designs. + +.. _SHA-1: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA1Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: # The sha module is deprecated in Python 2.6, so use hashlib when possible. @@ -52,5 +51,48 @@ except ImportError: import sha hashFactory = sha -digest_size = 20 -block_size = 64 +class SHA1Hash(HashAlgo): + """Class that implements a SHA-1 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha1 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) oiw(14) secsig(3) + #: algorithms(2) 26 + #: } + #: + #: This value uniquely identifies the SHA-1 algorithm. + oid = b('\x06\x05\x2b\x0e\x03\x02\x1a') + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA1Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA1Hash.update()`. + Optional. + + :Return: A `SHA1Hash` object + """ + return SHA1Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA1Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA1Hash.block_size + + diff --git a/lib/Crypto/Hash/SHA224.py b/lib/Crypto/Hash/SHA224.py index 872ed81..959b56d 100644 --- a/lib/Crypto/Hash/SHA224.py +++ b/lib/Crypto/Hash/SHA224.py @@ -18,31 +18,28 @@ # SOFTWARE. # =================================================================== -# Just use the SHA module from the Python standard library +"""SHA-224 cryptographic hash algorithm. -__revision__ = "$Id$" +SHA-224 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 224 bit digest of a message. -__all__ = ['new', 'digest_size'] + >>> from Crypto.Hash import SHA224 + >>> + >>> h = SHA224.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * +*SHA* stands for Secure Hash Algorithm. -# The OID for SHA-224 is: -# -# id-sha224 OBJECT IDENTIFIER ::= { -# joint-iso-itu-t(2) -# country(16) us(840) organization(1) gov(101) csor(3) -# nistalgorithm(4) hashalgs(2) 4 -# } -oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') - -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA224Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: import hashlib @@ -52,5 +49,47 @@ except ImportError: from Crypto.Hash import _SHA224 hashFactory = _SHA224 -digest_size = 28 -block_size = 64 +class SHA224Hash(HashAlgo): + """Class that implements a SHA-224 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha224 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 4 + #: } + #: + #: This value uniquely identifies the SHA-224 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') + + digest_size = 28 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA224Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA224Hash.update()`. + Optional. + + :Return: A `SHA224Hash` object + """ + return SHA224Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA224Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA224Hash.block_size + diff --git a/lib/Crypto/Hash/SHA256.py b/lib/Crypto/Hash/SHA256.py index f583ed9..b0a99b3 100644 --- a/lib/Crypto/Hash/SHA256.py +++ b/lib/Crypto/Hash/SHA256.py @@ -18,29 +18,28 @@ # SOFTWARE. # =================================================================== -__revision__ = "$Id$" +"""SHA-256 cryptographic hash algorithm. -__all__ = ['new', 'digest_size'] +SHA-256 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 256 bit digest of a message. -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * + >>> from Crypto.Hash import SHA256 + >>> + >>> h = SHA256.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -# The OID for SHA-256 is: -# -# id-sha256 OBJECT IDENTIFIER ::= { -# joint-iso-itu-t(2) country(16) us(840) organization(1) -# gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 -# } -# -oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01') +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA256Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: import hashlib @@ -50,6 +49,47 @@ except ImportError: from Crypto.Hash import _SHA256 hashFactory = _SHA256 -digest_size = 32 -block_size = 64 +class SHA256Hash(HashAlgo): + """Class that implements a SHA-256 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha256 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) + #: gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 + #: } + #: + #: This value uniquely identifies the SHA-256 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01') + + digest_size = 32 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA256Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA256Hash.update()`. + Optional. + + :Return: A `SHA256Hash` object + """ + return SHA256Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA256Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA256Hash.block_size diff --git a/lib/Crypto/Hash/SHA384.py b/lib/Crypto/Hash/SHA384.py index 1549e8e..3490b02 100644 --- a/lib/Crypto/Hash/SHA384.py +++ b/lib/Crypto/Hash/SHA384.py @@ -18,31 +18,28 @@ # SOFTWARE. # =================================================================== -# Just use the SHA module from the Python standard library +"""SHA-384 cryptographic hash algorithm. -__revision__ = "$Id$" +SHA-384 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 384 bit digest of a message. -__all__ = ['new', 'digest_size'] + >>> from Crypto.Hash import SHA384 + >>> + >>> h = SHA384.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * +*SHA* stands for Secure Hash Algorithm. -# The OID for SHA-384 is: -# -# id-sha384 OBJECT IDENTIFIER ::= { -# joint-iso-itu-t(2) -# country(16) us(840) organization(1) gov(101) csor(3) -# nistalgorithm(4) hashalgs(2) 2 -# } -oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') - -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA384Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: import hashlib @@ -52,6 +49,48 @@ except ImportError: from Crypto.Hash import _SHA384 hashFactory = _SHA384 -digest_size = 48 -block_size = 128 +class SHA384Hash(HashAlgo): + """Class that implements a SHA-384 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha384 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 2 + #: } + #: + #: This value uniquely identifies the SHA-384 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') + + digest_size = 48 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA384Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA384Hash.update()`. + Optional. + + :Return: A `SHA384Hash` object + """ + return SHA384Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA384Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA384Hash.block_size + diff --git a/lib/Crypto/Hash/SHA512.py b/lib/Crypto/Hash/SHA512.py index 182ec74..d57548d 100644 --- a/lib/Crypto/Hash/SHA512.py +++ b/lib/Crypto/Hash/SHA512.py @@ -18,31 +18,28 @@ # SOFTWARE. # =================================================================== -# Just use the SHA module from the Python standard library +"""SHA-512 cryptographic hash algorithm. -__revision__ = "$Id$" +SHA-512 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 512 bit digest of a message. -__all__ = ['new', 'digest_size'] + >>> from Crypto.Hash import SHA512 + >>> + >>> h = SHA512.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() -from Crypto.Util.wrapper import Wrapper -from Crypto.Util.py3compat import * +*SHA* stands for Secure Hash Algorithm. -# The OID for SHA-512 is: -# -# id-sha512 OBJECT IDENTIFIER ::= { -# joint-iso-itu-t(2) -# country(16) us(840) organization(1) gov(101) csor(3) -# nistalgorithm(4) hashalgs(2) 3 -# } -oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03') - -def new(data=b("")): - obj = Wrapper(hashFactory, data) - obj.oid = oid - obj.new = globals()['new'] - if not hasattr(obj, 'digest_size'): - obj.digest_size = digest_size - return obj +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA512Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo try: import hashlib @@ -52,6 +49,47 @@ except ImportError: from Crypto.Hash import _SHA512 hashFactory = _SHA512 -digest_size = 64 -block_size = 128 +class SHA512Hash(HashAlgo): + """Class that implements a SHA-512 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha512 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) + #: country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 + #: } + #: + #: This value uniquely identifies the SHA-512 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03') + + digest_size = 64 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA512Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA512Hash.update()`. + Optional. + + :Return: A `SHA512Hash` object + """ + return SHA512Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA512Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA512Hash.block_size diff --git a/lib/Crypto/Hash/__init__.py b/lib/Crypto/Hash/__init__.py index f704b51..4582c66 100644 --- a/lib/Crypto/Hash/__init__.py +++ b/lib/Crypto/Hash/__init__.py @@ -20,39 +20,33 @@ """Hashing algorithms -Hash functions take arbitrary strings as input, and produce an output -of fixed size that is dependent on the input; it should never be -possible to derive the input data given only the hash function's -output. Hash functions can be used simply as a checksum, or, in +Hash functions take arbitrary binary strings as input, and produce a random-like output +of fixed size that is dependent on the input; it should be practically infeasible +to derive the original input data given only the hash function's +output. In other words, the hash function is *one-way*. + +It should also not be practically feasible to find a second piece of data +(a *second pre-image*) whose hash is the same as the original message +(*weak collision resistance*). + +Finally, it should not be feasible to find two arbitrary messages with the +same hash (*strong collision resistance*). + +The output of the hash function is called the *digest* of the input message. +In general, the security of a hash function is related to the length of the +digest. If the digest is *n* bits long, its security level is roughly comparable +to the the one offered by an *n/2* bit encryption algorithm. + +Hash functions can be used simply as a integrity check, or, in association with a public-key algorithm, can be used to implement digital signatures. -The hashing modules here all support the interface described in PEP -247, "API for Cryptographic Hash Functions". - -Submodules: - -Crypto.Hash.HMAC - RFC 2104. Keyed-Hashing for Message Authentication. -Crypto.Hash.MD2 - RFC1319. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is both slow and insecure. -Crypto.Hash.MD4 - RFC1320. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is insecure. -Crypto.Hash.MD5 - RFC1321. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is insecure. -Crypto.Hash.RIPEMD - RACE Integrity Primitives Evaluation Message Digest algorithm, with a 160 bit digest. -Crypto.Hash.SHA - Secure Hash Algorithm 1 (SHA-1), with a 160 bit digest. Published in FIPS PUB 180-1/2/3. -Crypto.Hash.SHA224 - Secure Hash Algorithm 2 (SHA-2 family), with a 224 bit digest. Published in FIPS PUB 180-2/3. -Crypto.Hash.SHA256 - Secure Hash Algorithm 2 (SHA-2 family), with a 256 bit digest. Published in FIPS PUB 180-2/3. -Crypto.Hash.SHA384 - Secure Hash Algorithm 2 (SHA-2 family), with a 384 bit digest. Published in FIPS PUB 180-2/3. -Crypto.Hash.SHA512 - Secure Hash Algorithm 2 (SHA-2 family), with a 512 bit digest. Published in FIPS PUB 180-2/3. +The hashing modules here all support the interface described in `PEP +247`_ , "API for Cryptographic Hash Functions". + +.. _`PEP 247` : http://www.python.org/dev/peps/pep-0247/ +:undocumented: _MD2, _MD4, _RIPEMD160, _SHA224, _SHA256, _SHA384, _SHA512 """ __all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA', diff --git a/lib/Crypto/Hash/hashalgo.py b/lib/Crypto/Hash/hashalgo.py new file mode 100644 index 0000000..b38b3a6 --- /dev/null +++ b/lib/Crypto/Hash/hashalgo.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# +# 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. +# =================================================================== + +from binascii import hexlify + +class HashAlgo: + """A generic class for an abstract cryptographic hash algorithm. + + :undocumented: block_size + """ + + #: The size of the resulting hash in bytes. + digest_size = None + #: The internal block size of the hash algorithm in bytes. + block_size = None + + def __init__(self, hashFactory, data=None): + """Initialize the hash object. + + :Parameters: + hashFactory : callable + An object that will generate the actual hash implementation. + *hashFactory* must have a *new()* method, or must be directly + callable. + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `update()`. + """ + if hasattr(hashFactory, 'new'): + self._hash = hashFactory.new() + else: + self._hash = hashFactory() + if data: + self.update(data) + + def update(self, data): + """Continue hashing of a message by consuming the next chunk of data. + + Repeated calls are equivalent to a single call with the concatenation + of all the arguments. In other words: + + >>> m.update(a); m.update(b) + + is equivalent to: + + >>> m.update(a+b) + + :Parameters: + data : byte string + The next chunk of the message being hashed. + """ + return self._hash.update(data) + + def digest(self): + """Return the **binary** (non-printable) digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + You can continue updating the object after calling this function. + + :Return: A byte string of `digest_size` bytes. It may contain non-ASCII + characters, including null bytes. + """ + return self._hash.digest() + + def hexdigest(self): + """Return the **printable** digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + + :Return: A string of 2* `digest_size` characters. It contains only + hexadecimal ASCII digits. + """ + return self._hash.hexdigest() + + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :Return: A hash object of the same type + """ + return self._hash.copy() + + def new(self, data=None): + """Return a fresh instance of the hash object. + + Unlike the `copy` method, the internal state of the object is empty. + + :Parameters: + data : byte string + The next chunk of the message being hashed. + + :Return: A hash object of the same type + """ + pass + diff --git a/lib/Crypto/Util/wrapper.py b/lib/Crypto/Util/wrapper.py deleted file mode 100644 index 1090fc7..0000000 --- a/lib/Crypto/Util/wrapper.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# wrapper.py: Small class to wrap an object, instantiated from a class -# or generated by a module. -# -# =================================================================== -# 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. -# -# 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. -# =================================================================== -# - -__all__ = [ 'Wrapper' ] - -class Wrapper: - ''' - Wrapper for an object, instantiated from a class - or from a call to a new() function in a module. - ''' - def __init__(self, wrapped, *args): - """ - wrapped is either a class or a module with a new() function. - """ - if hasattr(wrapped, 'new'): - self._wrapped = wrapped.new(*args) - else: - self._wrapped = wrapped(*args) - - def __getattr__(self, name): - try: - return getattr(getattr(self,'_wrapped'),name) - except AttributeError: - if hasattr(self, name): - return getattr(self,name) - raise - -- cgit v1.2.1