|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
MD4 is specified in RFC1320_ and produces the 128 bit digest of a message. |
|
|
|
>>> from Crypto.Hash import MD4 |
|
>>> |
|
>>> h = MD4.new() |
|
>>> h.update(b'Hello') |
|
>>> print h.hexdigest() |
|
|
|
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 |
|
""" |
|
|
|
from Crypto.Util.py3compat import bord |
|
|
|
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, |
|
VoidPointer, SmartPointer, |
|
create_string_buffer, |
|
get_raw_buffer, c_size_t, |
|
c_uint8_ptr) |
|
|
|
_raw_md4_lib = load_pycryptodome_raw_lib( |
|
"Crypto.Hash._MD4", |
|
""" |
|
int md4_init(void **shaState); |
|
int md4_destroy(void *shaState); |
|
int md4_update(void *hs, |
|
const uint8_t *buf, |
|
size_t len); |
|
int md4_digest(const void *shaState, |
|
uint8_t digest[20]); |
|
int md4_copy(const void *src, void *dst); |
|
""") |
|
|
|
|
|
class MD4Hash(object): |
|
"""Class that implements an MD4 hash |
|
""" |
|
|
|
|
|
digest_size = 16 |
|
|
|
block_size = 64 |
|
|
|
oid = "1.2.840.113549.2.4" |
|
|
|
def __init__(self, data=None): |
|
state = VoidPointer() |
|
result = _raw_md4_lib.md4_init(state.address_of()) |
|
if result: |
|
raise ValueError("Error %d while instantiating MD4" |
|
% result) |
|
self._state = SmartPointer(state.get(), |
|
_raw_md4_lib.md4_destroy) |
|
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/byte array/memoryview |
|
The next chunk of the message being hashed. |
|
""" |
|
|
|
result = _raw_md4_lib.md4_update(self._state.get(), |
|
c_uint8_ptr(data), |
|
c_size_t(len(data))) |
|
if result: |
|
raise ValueError("Error %d while instantiating MD4" |
|
% result) |
|
|
|
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. |
|
""" |
|
|
|
bfr = create_string_buffer(self.digest_size) |
|
result = _raw_md4_lib.md4_digest(self._state.get(), |
|
bfr) |
|
if result: |
|
raise ValueError("Error %d while instantiating MD4" |
|
% result) |
|
|
|
return get_raw_buffer(bfr) |
|
|
|
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 "".join(["%02x" % bord(x) for x in self.digest()]) |
|
|
|
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 |
|
""" |
|
|
|
clone = MD4Hash() |
|
result = _raw_md4_lib.md4_copy(self._state.get(), |
|
clone._state.get()) |
|
if result: |
|
raise ValueError("Error %d while copying MD4" % result) |
|
return clone |
|
|
|
def new(self, data=None): |
|
return MD4Hash(data) |
|
|
|
|
|
def new(data=None): |
|
"""Return a fresh instance of the hash object. |
|
|
|
:Parameters: |
|
data : byte string/byte array/memoryview |
|
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) |
|
|
|
|
|
digest_size = MD4Hash.digest_size |
|
|
|
|
|
block_size = MD4Hash.block_size |
|
|