Skip to content

Commit

Permalink
support of md4 hash using pure python code
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessandroZ committed Jun 5, 2024
1 parent 46ac8c6 commit 3f035a2
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 4 deletions.
19 changes: 15 additions & 4 deletions Windows/lazagne/config/DPAPI/masterkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
from .eater import DataStruct, Eater
from collections import defaultdict

import binascii
import codecs
import hashlib
import struct
import os

from lazagne.config.constant import constant
from lazagne.config.crypto.md4 import MD4


class MasterKey(DataStruct):
Expand Down Expand Up @@ -66,10 +68,19 @@ def decrypt_with_password(self, sid, pwd):
except Exception:
return

for algo in ["sha1", "md4"]:
self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new(algo, pwd).digest())
if self.decrypted:
break
# sha1
self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new("sha1", pwd).digest())
if self.decrypted:
return

# md4
self.decrypt_with_hash(sid=sid, pwdhash=binascii.unhexlify(MD4(pwd).hexdigest()))

# hashlib does not support md4 hash anymore
# for algo in ["sha1", "md4"]:
# self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new(algo, pwd).digest())
# if self.decrypted:
# break

def decrypt_with_key(self, pwdhash):
"""
Expand Down
114 changes: 114 additions & 0 deletions Windows/lazagne/config/crypto/md4.py
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

0 comments on commit 3f035a2

Please sign in to comment.