md5-nohashlib-fast.py with syntax coloring

This HTML file was generated with Kalle's syntaxcolor.py


   1"""
   2Faster version of md5-nohashlib.py. Doesn't support messages > 55 bytes.
   3"""
   4
   5import sys
   6import struct
   7
   8HELP_TEXT = """\
   9Calculates the MD5 hash of a string without hashlib etc.
  10Argument: string (only 7-bit ASCII characters are allowed)\
  11"""
  12
  13class uint32(int):
  14    """
  15    This works, but I'm not sure if this is the best way to do 32-bit
  16    arithmetic. (I don't have much experience with object-oriented
  17    programming.)
  18    """
  19
  20    def __new__(s, v = 0):
  21        return int.__new__(s, v & 0xffffffff)
  22
  23    def __invert__(s):
  24        return uint32(~int(s))
  25
  26    def __and__(s, v):
  27        return uint32(int(s) & v)
  28
  29    def __or__(s, v):
  30        return uint32(int(s) | v)
  31
  32    def __xor__(s, v):
  33        return uint32(int(s) ^ v)
  34
  35    def __add__(s, v):
  36        return uint32(int(s) + v)
  37
  38    def __lshift__(s, v):
  39        return uint32(int(s) << v)
  40
  41    def __rshift__(s, v):
  42        return uint32(int(s) >> v)
  43
  44def rol(number, amount):
  45    """
  46    Rotate number left using unsigned 32-bit integer arithmetic.
  47    """
  48
  49    return (number << amount) | (number >> (32 - amount))
  50
  51def MD5_hash(message):
  52    len_ = len(message)
  53
  54    # restrict message length to 55 bytes to limit number of chunks to hash
  55    # to one
  56    if len_ > 55:
  57        exit("Messages longer than 55 bytes aren't supported.")
  58
  59    # add one "1" bit; pad to 56 bytes with "0" bits; add length in bits as
  60    # little-endian 64-bit integer
  61    message += \
  62    b"\x80" + (55 - len_) * b"\x00" + struct.pack("<Q", len_ * 8)
  63
  64    # split message to sixteen uint32
  65    message = struct.unpack("<16I", message)
  66    (m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, ma, mb, mc, md, me, mf) = \
  67    (uint32(part) for part in message)
  68
  69    # set initial state of algorithm
  70    s0 = uint32(0x67452301)
  71    s1 = uint32(0xEFCDAB89)
  72    s2 = uint32(0x98BADCFE)
  73    s3 = uint32(0x10325476)
  74
  75    # rounds 1-16
  76
  77    s0 = s1 + rol(s0 + 0xd76aa478 + (s1&s2 | ~s1&s3) + m0,  7)
  78    s3 = s0 + rol(s3 + 0xe8c7b756 + (s0&s1 | ~s0&s2) + m1, 12)
  79    s2 = s3 + rol(s2 + 0x242070db + (s3&s0 | ~s3&s1) + m2, 17)
  80    s1 = s2 + rol(s1 + 0xc1bdceee + (s2&s3 | ~s2&s0) + m3, 22)
  81
  82    s0 = s1 + rol(s0 + 0xf57c0faf + (s1&s2 | ~s1&s3) + m4,  7)
  83    s3 = s0 + rol(s3 + 0x4787c62a + (s0&s1 | ~s0&s2) + m5, 12)
  84    s2 = s3 + rol(s2 + 0xa8304613 + (s3&s0 | ~s3&s1) + m6, 17)
  85    s1 = s2 + rol(s1 + 0xfd469501 + (s2&s3 | ~s2&s0) + m7, 22)
  86
  87    s0 = s1 + rol(s0 + 0x698098d8 + (s1&s2 | ~s1&s3) + m8,  7)
  88    s3 = s0 + rol(s3 + 0x8b44f7af + (s0&s1 | ~s0&s2) + m9, 12)
  89    s2 = s3 + rol(s2 + 0xffff5bb1 + (s3&s0 | ~s3&s1) + ma, 17)
  90    s1 = s2 + rol(s1 + 0x895cd7be + (s2&s3 | ~s2&s0) + mb, 22)
  91
  92    s0 = s1 + rol(s0 + 0x6b901122 + (s1&s2 | ~s1&s3) + mc,  7)
  93    s3 = s0 + rol(s3 + 0xfd987193 + (s0&s1 | ~s0&s2) + md, 12)
  94    s2 = s3 + rol(s2 + 0xa679438e + (s3&s0 | ~s3&s1) + me, 17)
  95    s1 = s2 + rol(s1 + 0x49b40821 + (s2&s3 | ~s2&s0) + mf, 22)
  96
  97    # rounds 17-32
  98
  99    s0 = s1 + rol(s0 + 0xf61e2562 + (s3&s1 | ~s3&s2) + m1,  5)
 100    s3 = s0 + rol(s3 + 0xc040b340 + (s2&s0 | ~s2&s1) + m6,  9)
 101    s2 = s3 + rol(s2 + 0x265e5a51 + (s1&s3 | ~s1&s0) + mb, 14)
 102    s1 = s2 + rol(s1 + 0xe9b6c7aa + (s0&s2 | ~s0&s3) + m0, 20)
 103
 104    s0 = s1 + rol(s0 + 0xd62f105d + (s3&s1 | ~s3&s2) + m5,  5)
 105    s3 = s0 + rol(s3 + 0x02441453 + (s2&s0 | ~s2&s1) + ma,  9)
 106    s2 = s3 + rol(s2 + 0xd8a1e681 + (s1&s3 | ~s1&s0) + mf, 14)
 107    s1 = s2 + rol(s1 + 0xe7d3fbc8 + (s0&s2 | ~s0&s3) + m4, 20)
 108
 109    s0 = s1 + rol(s0 + 0x21e1cde6 + (s3&s1 | ~s3&s2) + m9,  5)
 110    s3 = s0 + rol(s3 + 0xc33707d6 + (s2&s0 | ~s2&s1) + me,  9)
 111    s2 = s3 + rol(s2 + 0xf4d50d87 + (s1&s3 | ~s1&s0) + m3, 14)
 112    s1 = s2 + rol(s1 + 0x455a14ed + (s0&s2 | ~s0&s3) + m8, 20)
 113
 114    s0 = s1 + rol(s0 + 0xa9e3e905 + (s3&s1 | ~s3&s2) + md,  5)
 115    s3 = s0 + rol(s3 + 0xfcefa3f8 + (s2&s0 | ~s2&s1) + m2,  9)
 116    s2 = s3 + rol(s2 + 0x676f02d9 + (s1&s3 | ~s1&s0) + m7, 14)
 117    s1 = s2 + rol(s1 + 0x8d2a4c8a + (s0&s2 | ~s0&s3) + mc, 20)
 118
 119    # rounds 33-48
 120
 121    s0 = s1 + rol(s0 + 0xfffa3942 + (s1^s2^s3) + m5,  4)
 122    s3 = s0 + rol(s3 + 0x8771f681 + (s0^s1^s2) + m8, 11)
 123    s2 = s3 + rol(s2 + 0x6d9d6122 + (s0^s1^s3) + mb, 16)
 124    s1 = s2 + rol(s1 + 0xfde5380c + (s0^s2^s3) + me, 23)
 125
 126    s0 = s1 + rol(s0 + 0xa4beea44 + (s1^s2^s3) + m1,  4)
 127    s3 = s0 + rol(s3 + 0x4bdecfa9 + (s0^s1^s2) + m4, 11)
 128    s2 = s3 + rol(s2 + 0xf6bb4b60 + (s0^s1^s3) + m7, 16)
 129    s1 = s2 + rol(s1 + 0xbebfbc70 + (s0^s2^s3) + ma, 23)
 130
 131    s0 = s1 + rol(s0 + 0x289b7ec6 + (s1^s2^s3) + md,  4)
 132    s3 = s0 + rol(s3 + 0xeaa127fa + (s0^s1^s2) + m0, 11)
 133    s2 = s3 + rol(s2 + 0xd4ef3085 + (s0^s1^s3) + m3, 16)
 134    s1 = s2 + rol(s1 + 0x04881d05 + (s0^s2^s3) + m6, 23)
 135
 136    s0 = s1 + rol(s0 + 0xd9d4d039 + (s1^s2^s3) + m9,  4)
 137    s3 = s0 + rol(s3 + 0xe6db99e5 + (s0^s1^s2) + mc, 11)
 138    s2 = s3 + rol(s2 + 0x1fa27cf8 + (s0^s1^s3) + mf, 16)
 139    s1 = s2 + rol(s1 + 0xc4ac5665 + (s0^s2^s3) + m2, 23)
 140
 141    # rounds 49-64
 142
 143    s0 = s1 + rol(s0 + 0xf4292244 + (s2 ^ (s1|~s3)) + m0,  6)
 144    s3 = s0 + rol(s3 + 0x432aff97 + (s1 ^ (s0|~s2)) + m7, 10)
 145    s2 = s3 + rol(s2 + 0xab9423a7 + (s0 ^ (s3|~s1)) + me, 15)
 146    s1 = s2 + rol(s1 + 0xfc93a039 + (s3 ^ (s2|~s0)) + m5, 21)
 147
 148    s0 = s1 + rol(s0 + 0x655b59c3 + (s2 ^ (s1|~s3)) + mc,  6)
 149    s3 = s0 + rol(s3 + 0x8f0ccc92 + (s1 ^ (s0|~s2)) + m3, 10)
 150    s2 = s3 + rol(s2 + 0xffeff47d + (s0 ^ (s3|~s1)) + ma, 15)
 151    s1 = s2 + rol(s1 + 0x85845dd1 + (s3 ^ (s2|~s0)) + m1, 21)
 152
 153    s0 = s1 + rol(s0 + 0x6fa87e4f + (s2 ^ (s1|~s3)) + m8,  6)
 154    s3 = s0 + rol(s3 + 0xfe2ce6e0 + (s1 ^ (s0|~s2)) + mf, 10)
 155    s2 = s3 + rol(s2 + 0xa3014314 + (s0 ^ (s3|~s1)) + m6, 15)
 156    s1 = s2 + rol(s1 + 0x4e0811a1 + (s3 ^ (s2|~s0)) + md, 21)
 157
 158    s0 = s1 + rol(s0 + 0xf7537e82 + (s2 ^ (s1|~s3)) + m4 , 6)
 159    s3 = s0 + rol(s3 + 0xbd3af235 + (s1 ^ (s0|~s2)) + mb, 10)
 160    s2 = s3 + rol(s2 + 0x2ad7d2bb + (s0 ^ (s3|~s1)) + m2, 15)
 161    s1 = s2 + rol(s1 + 0xeb86d391 + (s3 ^ (s2|~s0)) + m9, 21)
 162
 163    # add initial state to current state
 164    s0 += 0x67452301
 165    s1 += 0xEFCDAB89
 166    s2 += 0x98BADCFE
 167    s3 += 0x10325476
 168
 169    # the final state of the algorithm is the hash
 170    hash = (s0, s1, s2, s3)
 171
 172    # encode hash to binary as four little-endian uint32
 173    binaryHash = b"".join(struct.pack("<I", number) for number in hash)
 174
 175    # convert hash to hexadecimal
 176    return "".join(format(byte, "02x") for byte in binaryHash)
 177
 178def main():
 179    if len(sys.argv) != 2:
 180        exit(HELP_TEXT)
 181
 182    message = sys.argv[1]
 183
 184    try:
 185        messageBytes = message.encode("ascii")
 186    except UnicodeError:
 187        exit("Only 7-bit ASCII characters are allowed.")
 188
 189    print(MD5_hash(messageBytes))
 190
 191if __name__ == "__main__":
 192    main()