The Algorithms logo
The Algorithms
AboutDonate

Md 5

D
C
W
c
9
C
C
n
and 3 more contributors
import math


def rearrange(bit_string_32):
    """[summary]
    Regroups the given binary string.

    Arguments:
        bitString32 {[string]} -- [32 bit binary]

    Raises:
    ValueError -- [if the given string not are 32 bit binary string]

    Returns:
        [string] -- [32 bit binary string]
    >>> rearrange('1234567890abcdfghijklmnopqrstuvw')
    'pqrstuvwhijklmno90abcdfg12345678'
    """

    if len(bit_string_32) != 32:
        raise ValueError("Need length 32")
    new_string = ""
    for i in [3, 2, 1, 0]:
        new_string += bit_string_32[8 * i : 8 * i + 8]
    return new_string


def reformat_hex(i):
    """[summary]
    Converts the given integer into 8-digit hex number.

    Arguments:
            i {[int]} -- [integer]
    >>> reformat_hex(666)
    '9a020000'
    """

    hexrep = format(i, "08x")
    thing = ""
    for i in [3, 2, 1, 0]:
        thing += hexrep[2 * i : 2 * i + 2]
    return thing


def pad(bit_string):
    """[summary]
    Fills up the binary string to a 512 bit binary string

    Arguments:
            bitString {[string]} -- [binary string]

    Returns:
            [string] -- [binary string]
    """
    start_length = len(bit_string)
    bit_string += "1"
    while len(bit_string) % 512 != 448:
        bit_string += "0"
    last_part = format(start_length, "064b")
    bit_string += rearrange(last_part[32:]) + rearrange(last_part[:32])
    return bit_string


def get_block(bit_string):
    """[summary]
    Iterator:
            Returns by each call a list of length 16 with the 32 bit
            integer blocks.

    Arguments:
            bit_string {[string]} -- [binary string >= 512]
    """

    curr_pos = 0
    while curr_pos < len(bit_string):
        curr_part = bit_string[curr_pos : curr_pos + 512]
        my_splits = []
        for i in range(16):
            my_splits.append(int(rearrange(curr_part[32 * i : 32 * i + 32]), 2))
        yield my_splits
        curr_pos += 512


def not32(i):
    """
    >>> not32(34)
    4294967261
    """
    i_str = format(i, "032b")
    new_str = ""
    for c in i_str:
        new_str += "1" if c == "0" else "0"
    return int(new_str, 2)


def sum32(a, b):
    return (a + b) % 2**32


def leftrot32(i, s):
    return (i << s) ^ (i >> (32 - s))


def md5me(test_string):
    """[summary]
    Returns a 32-bit hash code of the string 'testString'

    Arguments:
            testString {[string]} -- [message]
    """

    bs = ""
    for i in test_string:
        bs += format(ord(i), "08b")
    bs = pad(bs)

    tvals = [int(2**32 * abs(math.sin(i + 1))) for i in range(64)]

    a0 = 0x67452301
    b0 = 0xEFCDAB89
    c0 = 0x98BADCFE
    d0 = 0x10325476

    s = [
        7,
        12,
        17,
        22,
        7,
        12,
        17,
        22,
        7,
        12,
        17,
        22,
        7,
        12,
        17,
        22,
        5,
        9,
        14,
        20,
        5,
        9,
        14,
        20,
        5,
        9,
        14,
        20,
        5,
        9,
        14,
        20,
        4,
        11,
        16,
        23,
        4,
        11,
        16,
        23,
        4,
        11,
        16,
        23,
        4,
        11,
        16,
        23,
        6,
        10,
        15,
        21,
        6,
        10,
        15,
        21,
        6,
        10,
        15,
        21,
        6,
        10,
        15,
        21,
    ]

    for m in get_block(bs):
        a = a0
        b = b0
        c = c0
        d = d0
        for i in range(64):
            if i <= 15:
                # f = (B & C) | (not32(B) & D)
                f = d ^ (b & (c ^ d))
                g = i
            elif i <= 31:
                # f = (D & B) | (not32(D) & C)
                f = c ^ (d & (b ^ c))
                g = (5 * i + 1) % 16
            elif i <= 47:
                f = b ^ c ^ d
                g = (3 * i + 5) % 16
            else:
                f = c ^ (b | not32(d))
                g = (7 * i) % 16
            dtemp = d
            d = c
            c = b
            b = sum32(b, leftrot32((a + f + tvals[i] + m[g]) % 2**32, s[i]))
            a = dtemp
        a0 = sum32(a0, a)
        b0 = sum32(b0, b)
        c0 = sum32(c0, c)
        d0 = sum32(d0, d)

    digest = reformat_hex(a0) + reformat_hex(b0) + reformat_hex(c0) + reformat_hex(d0)
    return digest


def test():
    assert md5me("") == "d41d8cd98f00b204e9800998ecf8427e"
    assert (
        md5me("The quick brown fox jumps over the lazy dog")
        == "9e107d9d372bb6826bd81d3542a419d6"
    )
    print("Success.")


if __name__ == "__main__":
    test()
    import doctest

    doctest.testmod()