forked from AlessandroZ/LaZagne
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support of md4 hash using pure python code
- Loading branch information
1 parent
46ac8c6
commit 3f035a2
Showing
2 changed files
with
129 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright © 2019 James Seo <james@equiv.tech> (github.com/kangtastic). | ||
# | ||
# This file is released under the WTFPL, version 2 (wtfpl.net). | ||
# | ||
# md4.py: An implementation of the MD4 hash algorithm in pure Python 3. | ||
# | ||
# Description: Zounds! Yet another rendition of pseudocode from RFC1320! | ||
# Bonus points for the algorithm literally being from 1992. | ||
# | ||
|
||
# From https://gist.github.com/kangtastic/c3349fc4f9d659ee362b12d7d8c639b6 | ||
|
||
import struct | ||
|
||
|
||
class MD4: | ||
"""An implementation of the MD4 hash algorithm.""" | ||
|
||
width = 32 | ||
mask = 0xFFFFFFFF | ||
|
||
# Unlike, say, SHA-1, MD4 uses little-endian. Fascinating! | ||
h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476] | ||
|
||
def __init__(self, msg=None): | ||
""":param ByteString msg: The message to be hashed.""" | ||
if msg is None: | ||
msg = b"" | ||
|
||
self.msg = msg | ||
|
||
# Pre-processing: Total length is a multiple of 512 bits. | ||
ml = len(msg) * 8 | ||
msg += b"\x80" | ||
msg += b"\x00" * (-(len(msg) + 8) % 64) | ||
msg += struct.pack("<Q", ml) | ||
|
||
# Process the message in successive 512-bit chunks. | ||
self._process([msg[i : i + 64] for i in range(0, len(msg), 64)]) | ||
|
||
def __repr__(self): | ||
if self.msg: | ||
return f"{self.__class__.__name__}({self.msg})" | ||
return f"{self.__class__.__name__}()" | ||
|
||
def __str__(self): | ||
return self.hexdigest() | ||
|
||
def __eq__(self, other): | ||
return self.h == other.h | ||
|
||
def bytes(self): | ||
""":return: The final hash value as a `bytes` object.""" | ||
return struct.pack("<4L", *self.h) | ||
|
||
def hexbytes(self): | ||
""":return: The final hash value as hexbytes.""" | ||
return self.hexdigest().encode() | ||
|
||
def hexdigest(self): | ||
""":return: The final hash value as a hexstring.""" | ||
return "".join(f"{value:02x}" for value in self.bytes()) | ||
|
||
def _process(self, chunks): | ||
for chunk in chunks: | ||
X, h = list(struct.unpack("<16I", chunk)), self.h.copy() | ||
|
||
# Round 1. | ||
Xi = [3, 7, 11, 19] | ||
for n in range(16): | ||
i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | ||
K, S = n, Xi[n % 4] | ||
hn = h[i] + MD4.F(h[j], h[k], h[l]) + X[K] | ||
h[i] = MD4.lrot(hn & MD4.mask, S) | ||
|
||
# Round 2. | ||
Xi = [3, 5, 9, 13] | ||
for n in range(16): | ||
i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | ||
K, S = n % 4 * 4 + n // 4, Xi[n % 4] | ||
hn = h[i] + MD4.G(h[j], h[k], h[l]) + X[K] + 0x5A827999 | ||
h[i] = MD4.lrot(hn & MD4.mask, S) | ||
|
||
# Round 3. | ||
Xi = [3, 9, 11, 15] | ||
Ki = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] | ||
for n in range(16): | ||
i, j, k, l = map(lambda x: x % 4, range(-n, -n + 4)) | ||
K, S = Ki[n], Xi[n % 4] | ||
hn = h[i] + MD4.H(h[j], h[k], h[l]) + X[K] + 0x6ED9EBA1 | ||
h[i] = MD4.lrot(hn & MD4.mask, S) | ||
|
||
self.h = [((v + n) & MD4.mask) for v, n in zip(self.h, h)] | ||
|
||
@staticmethod | ||
def F(x, y, z): | ||
return (x & y) | (~x & z) | ||
|
||
@staticmethod | ||
def G(x, y, z): | ||
return (x & y) | (x & z) | (y & z) | ||
|
||
@staticmethod | ||
def H(x, y, z): | ||
return x ^ y ^ z | ||
|
||
@staticmethod | ||
def lrot(value, n): | ||
lbits, rbits = (value << n) & MD4.mask, value >> (MD4.width - n) | ||
return lbits | rbits | ||
|