# This is a collection of the four SHA algorithms: SHA-1, SHA-256,
# SHA-384, and SHA-512. They each produce a digest with a bit length
# according to their name (SHA-1 produces 160 bit though).

# SHA-1 and SHA-256 both take input with bit length < 2^64.
# SHA-384 and SHA-512 both take input with bit length < 2^128.

# Usage:
#   >>> import sha
#   >>> data = sha.str2bin("All your base are belong to us!")
#   >>> sha.digest(data, "sha-512")
#   '01001101001000000100100101101010001111100001110011111010001101110
#    01111000100101111111100011100001010101111010100111010011000100000
#    11111011011001101010111101101000101100011111001000100010010001010
#    11111111000110011101111100111011111011100001010111000101110101001
#    10010100111100100001101000100001101000000010100001100111001110000
#    10010000101110011111001000010111111110000101100111011111011110001
#    11111100000001111101110110100011110110111110101111000101110100001
#    011100110010001011001001001110001011101000111110011100111'
#   >>> sha.hex_digest(data, "sha-512")
#   '4d20496a3e1cfa373c4bfc70abd4e9883ed9abda2c7c88915fe33be77dc2b8ba9
#    94f21a21a0286738485cf90bfc2cefbc7f01f768f6faf1742e6459271747ce7'
# In the above you can substitute 'sha-512' with any of the four
# algorithms.

# NOTE: for educational purposes I have not contracted the four digest
# functions into, perhaps, one large function. This is to show exactly
# how each hash function works without unnecessary code cluttering.

# Copyright (c) 2009, Morten Kristensen (msk@nullpointer.dk)
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials
#       provided with the distribution.
#     * Neither the name of the Nullpointer.dk nor the names of its
#       contributors may be used to endorse or promote products
#       derived from this software without specific prior written
#       permission.
# 
# THIS SOFTWARE IS PROVIDED BY Morten Kristensen (msk@nullpointer.dk)
# ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Morten
# Kristensen (msk@nullpointer.dk) BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

####### Auxiliary functions #######

def binrep(num, block=8):
    binrep = bin(num)[2:]        
    rlen = len(binrep)
    pad = 0        
    while (rlen + pad) % block > 0:
        pad += 1
    return "0"*pad + binrep

def str2bin(data):
    binarr = bytearray(data.encode("utf-8"))
    res = ""
    for b in binarr:
        res += binrep(b)
    return res

def bin2hex(data, block=40):
    res = ""    
    for i in range(block):
        res += hex(bin2dec(word(i, data, 4)))[2:]
    return res

def hex2bin(data):
    res = ""
    for c in data:
        if c == "a":
            n = 10
        elif c == "b":
            n = 11
        elif c == "c":
            n = 12
        elif c == "d":
            n = 13 
        elif c == "e":
            n = 14
        elif c == "f":
            n = 15
        else:
            n = int(c)
        res += binrep(n, 4)
    return res

def AND(A, B, block=32):
    res = ""
    for i in range(block):
        if A[i] == "1" and B[i] == "1":
            res += "1"
        else:
            res += "0"
    return res

def OR(A, B, block=32):
    res = ""
    for i in range(block):
        if A[i] == "1" or B[i] == "1":
            res += "1"
        else:
            res += "0"
    return res

def XOR(A, B, block=32):
    res = ""
    for i in range(block):
        if A[i] == B[i]:
            res += "0"
        else:
            res += "1"
    return res

def NOT(A):
    res = ""
    for i in range(len(A)):
        if A[i] == "0":
            res += "1"
        else:
            res += "0"
    return res

def reverse(string):
    res = ""
    for c in string:
        res = c + res
    return res

def bin2dec(string, base=2):
    string = reverse(string)
    res = 0
    pos = 0
    for elm in string:
        res += int(elm)*(base**pos)
        pos += 1
    return res

def ADD(A, B, block=32):
    a = bin2dec(A)
    b = bin2dec(B)
    return binrep((a + b) % 2**block, block)

# Rotate left 
def ROTL(shift, binary):
    for i in range(shift):
        binary = binary[1:] + binary[0]
    return binary

# Rotate right
def ROTR(shift, binary):
    for i in range(shift):
        binary = binary[-1] + binary[0:-1] 
    return binary

# Shift right
def SHR(shift, binary):
    for i in range(shift):
        binary = "0" + binary[0:-1]
    return binary

def word(i, binary, block=32):
    return binary[(i*block):(i*block)+block]


def CH(B, C, D, block=32):
    return XOR(AND(B, C, block),
               AND(NOT(B), D, block), block)
    
def MAJ(B, C, D, block=32):
    return XOR(XOR(AND(B, C, block),
                   AND(B, D, block), block),
               AND(C, D, block), block)

def PARITY(B, C, D, block=32):
    return XOR(XOR(B, C, block), D, block)

def SUM(x, rot1, rot2, rot3, block=32):
    return XOR(XOR(ROTR(rot1, x), ROTR(rot2, x), block),
               ROTR(rot3, x), block)

def delta(x, rot1, rot2, rot3, block=32):
    return XOR(XOR(ROTR(rot1, x), ROTR(rot2, x), block),
               SHR(rot3, x), block)

####### SHA-1 functions #######

def sha1_pad(binary):
    bits = len(binary)
    if bits > 2**64-1:
        raise ValueError("max 2^64-1 bit input!")
    d = (447 - bits) % 512
    l = binrep(bits, 64)
    return binary + "1" + "0"*d  + l

def sha1_logic_f(B, C, D, t):
    if t >= 0 and t <= 19:
        return CH(B, C, D)
    elif (t >= 20 and t <= 39) or (t >= 60 and t <= 79):
        return PARITY(B, C, D)
    elif t >= 40 and t <= 59:
        return MAJ(B, C, D)

def sha1_k(t):
    if t >= 0 and t <= 19:
        return "01011010100000100111100110011001"
    elif t >= 20 and t <= 39:
        return "01101110110110011110101110100001"
    elif t >= 40 and t <= 59:
        return "10001111000110111011110011011100"
    elif t >= 60 and t <= 79:
        return "11001010011000101100000111010110"

####### SHA-256 functions #######

def sha256_pad(binary):
    return sha1_pad(binary)

def sha256_CH(B, C, D):
    return CH(B, C, D)

def sha256_MAJ(B, C, D):
    return MAJ(B, C, D)

def sha256_SUM0(x):
    return SUM(x, 2, 13, 22)

def sha256_SUM1(x):
    return SUM(x, 6, 11, 25)    

def sha256_delta0(x):
    return delta(x, 7, 18, 3)

def sha256_delta1(x):
    return delta(x, 17, 19, 10)

def sha256_k(t):
    consts = ["01000010100010100010111110011000",
              "01110001001101110100010010010001",
              "10110101110000001111101111001111",
              "11101001101101011101101110100101",
              "00111001010101101100001001011011",
              "01011001111100010001000111110001",
              "10010010001111111000001010100100",
              "10101011000111000101111011010101",
              "11011000000001111010101010011000",
              "00010010100000110101101100000001",
              "00100100001100011000010110111110",
              "01010101000011000111110111000011",
              "01110010101111100101110101110100",
              "10000000110111101011000111111110",
              "10011011110111000000011010100111",
              "11000001100110111111000101110100",
              "11100100100110110110100111000001",
              "11101111101111100100011110000110",
              "00001111110000011001110111000110",
              "00100100000011001010000111001100",
              "00101101111010010010110001101111",
              "01001010011101001000010010101010",
              "01011100101100001010100111011100",
              "01110110111110011000100011011010",
              "10011000001111100101000101010010",
              "10101000001100011100011001101101",
              "10110000000000110010011111001000",
              "10111111010110010111111111000111",
              "11000110111000000000101111110011",
              "11010101101001111001000101000111",
              "00000110110010100110001101010001",
              "00010100001010010010100101100111",
              "00100111101101110000101010000101",
              "00101110000110110010000100111000",
              "01001101001011000110110111111100",
              "01010011001110000000110100010011",
              "01100101000010100111001101010100",
              "01110110011010100000101010111011",
              "10000001110000101100100100101110",
              "10010010011100100010110010000101",
              "10100010101111111110100010100001",
              "10101000000110100110011001001011",
              "11000010010010111000101101110000",
              "11000111011011000101000110100011",
              "11010001100100101110100000011001",
              "11010110100110010000011000100100",
              "11110100000011100011010110000101",
              "00010000011010101010000001110000",
              "00011001101001001100000100010110",
              "00011110001101110110110000001000",
              "00100111010010000111011101001100",
              "00110100101100001011110010110101",
              "00111001000111000000110010110011",
              "01001110110110001010101001001010",
              "01011011100111001100101001001111",
              "01101000001011100110111111110011",
              "01110100100011111000001011101110",
              "01111000101001010110001101101111",
              "10000100110010000111100000010100",
              "10001100110001110000001000001000",
              "10010000101111101111111111111010",
              "10100100010100000110110011101011",
              "10111110111110011010001111110111",
              "11000110011100010111100011110010"]
    return consts[t]

####### SHA-384 functions #######

def sha384_pad(binary):
    bits = len(binary)
    if bits > 2**128-1:
        raise ValueError("max 2^128-1 bit input!")
    d = (895 - bits) % 1024
    l = binrep(bits, 128)
    return binary + "1" + "0"*d  + l

def sha384_CH(B, C, D):
    return CH(B, C, D, 64)

def sha384_MAJ(B, C, D):
    return MAJ(B, C, D, 64)

def sha384_SUM0(x):
    return SUM(x, 28, 34, 39, 64)

def sha384_SUM1(x):
    return SUM(x, 14, 18, 41, 64)    

def sha384_delta0(x):
    return delta(x, 1, 8, 7, 64)

def sha384_delta1(x):
    return delta(x, 19, 61, 6, 64)

def sha384_k(t):
    consts = ["0100001010001010001011111001100011010111001010001010111000100010",
              "0111000100110111010001001001000100100011111011110110010111001101",
              "1011010111000000111110111100111111101100010011010011101100101111",
              "1110100110110101110110111010010110000001100010011101101110111100",
              "0011100101010110110000100101101111110011010010001011010100111000",
              "0101100111110001000100011111000110110110000001011101000000011001",
              "1001001000111111100000101010010010101111000110010100111110011011",
              "1010101100011100010111101101010111011010011011011000000100011000",
              "1101100000000111101010101001100010100011000000110000001001000010",
              "0001001010000011010110110000000101000101011100000110111110111110",
              "0010010000110001100001011011111001001110111001001011001010001100",
              "0101010100001100011111011100001111010101111111111011010011100010",
              "0111001010111110010111010111010011110010011110111000100101101111",
              "1000000011011110101100011111111000111011000101101001011010110001",
              "1001101111011100000001101010011100100101110001110001001000110101",
              "1100000110011011111100010111010011001111011010010010011010010100",
              "1110010010011011011010011100000110011110111100010100101011010010",
              "1110111110111110010001111000011000111000010011110010010111100011",
              "0000111111000001100111011100011010001011100011001101010110110101",
              "0010010000001100101000011100110001110111101011001001110001100101",
              "0010110111101001001011000110111101011001001010110000001001110101",
              "0100101001110100100001001010101001101110101001101110010010000011",
              "0101110010110000101010011101110010111101010000011111101111010100",
              "0111011011111001100010001101101010000011000100010101001110110101",
              "1001100000111110010100010101001011101110011001101101111110101011",
              "1010100000110001110001100110110100101101101101000011001000010000",
              "1011000000000011001001111100100010011000111110110010000100111111",
              "1011111101011001011111111100011110111110111011110000111011100100",
              "1100011011100000000010111111001100111101101010001000111111000010",
              "1101010110100111100100010100011110010011000010101010011100100101",
              "0000011011001010011000110101000111100000000000111000001001101111",
              "0001010000101001001010010110011100001010000011100110111001110000",
              "0010011110110111000010101000010101000110110100100010111111111100",
              "0010111000011011001000010011100001011100001001101100100100100110",
              "0100110100101100011011011111110001011010110001000010101011101101",
              "0101001100111000000011010001001110011101100101011011001111011111",
              "0110010100001010011100110101010010001011101011110110001111011110",
              "0111011001101010000010101011101100111100011101111011001010101000",
              "1000000111000010110010010010111001000111111011011010111011100110",
              "1001001001110010001011001000010100010100100000100011010100111011",
              "1010001010111111111010001010000101001100111100010000001101100100",
              "1010100000011010011001100100101110111100010000100011000000000001",
              "1100001001001011100010110111000011010000111110001001011110010001",
              "1100011101101100010100011010001100000110010101001011111000110000",
              "1101000110010010111010000001100111010110111011110101001000011000",
              "1101011010011001000001100010010001010101011001011010100100010000",
              "1111010000001110001101011000010101010111011100010010000000101010",
              "0001000001101010101000000111000000110010101110111101000110111000",
              "0001100110100100110000010001011010111000110100101101000011001000",
              "0001111000110111011011000000100001010001010000011010101101010011",
              "0010011101001000011101110100110011011111100011101110101110011001",
              "0011010010110000101111001011010111100001100110110100100010101000",
              "0011100100011100000011001011001111000101110010010101101001100011",
              "0100111011011000101010100100101011100011010000011000101011001011",
              "0101101110011100110010100100111101110111011000111110001101110011",
              "0110100000101110011011111111001111010110101100101011100010100011",
              "0111010010001111100000101110111001011101111011111011001011111100",
              "0111100010100101011000110110111101000011000101110010111101100000",
              "1000010011001000011110000001010010100001111100001010101101110010",
              "1000110011000111000000100000100000011010011001000011100111101100",
              "1001000010111110111111111111101000100011011000110001111000101000",
              "1010010001010000011011001110101111011110100000101011110111101001",
              "1011111011111001101000111111011110110010110001100111100100010101",
              "1100011001110001011110001111001011100011011100100101001100101011",
              "1100101000100111001111101100111011101010001001100110000110011100",
              "1101000110000110101110001100011100100001110000001100001000000111",
              "1110101011011010011111011101011011001101111000001110101100011110",
              "1111010101111101010011110111111111101110011011101101000101111000",
              "0000011011110000011001111010101001110010000101110110111110111010",
              "0000101001100011011111011100010110100010110010001001100010100110",
              "0001000100111111100110000000010010111110111110010000110110101110",
              "0001101101110001000010110011010100010011000111000100011100011011",
              "0010100011011011011101111111010100100011000001000111110110000100",
              "0011001011001010101010110111101101000000110001110010010010010011",
              "0011110010011110101111100000101000010101110010011011111010111100",
              "0100001100011101011001111100010010011100000100000000110101001100",
              "0100110011000101110101001011111011001011001111100100001010110110",
              "0101100101111111001010011001110011111100011001010111111000101010",
              "0101111111001011011011111010101100111010110101101111101011101100",
              "0110110001000100000110011000110001001010010001110101100000010111"]
    return consts[t]

####### SHA-512 functions #######    

# These are the same as the SHA-384 functions.

####### Digest functions #######

def sha1_digest(binary):
    binary = sha1_pad(binary)
    h0 = "01100111010001010010001100000001"
    h1 = "11101111110011011010101110001001"
    h2 = "10011000101110101101110011111110"
    h3 = "00010000001100100101010001110110"
    h4 = "11000011110100101110000111110000"

    n = len(binary)//512
    for i in range(n):
        block = word(i, binary, 512)
        words = {k:word(k, block) for k in range(16)}
        for t in range(16, 80):
            words[t] = ROTL(1, XOR(XOR(XOR(words[t-3], words[t-8]),
                                       words[t-14]),
                                   words[t-16]))

        A = h0
        B = h1
        C = h2
        D = h3
        E = h4
        for t in range(80):
            tmp = ADD(ADD(ADD(ADD(ROTL(5, A),
                                  sha1_logic_f(B, C, D, t)),
                              E),
                          words[t]),
                      sha1_k(t))
            E = D
            D = C
            C = ROTL(30, B)
            B = A
            A = tmp
            
        h0 = ADD(h0, A)
        h1 = ADD(h1, B)
        h2 = ADD(h2, C)
        h3 = ADD(h3, D)
        h4 = ADD(h4, E)
            
    return h0 + h1 + h2 + h3 + h4

def sha256_digest(binary):
    binary = sha256_pad(binary)
    h0 = "01101010000010011110011001100111"
    h1 = "10111011011001111010111010000101"
    h2 = "00111100011011101111001101110010"
    h3 = "10100101010011111111010100111010"
    h4 = "01010001000011100101001001111111"
    h5 = "10011011000001010110100010001100"
    h6 = "00011111100000111101100110101011"
    h7 = "01011011111000001100110100011001"

    n = len(binary)//512
    for i in range(n):
        block = word(i, binary, 512)
        words = {k:word(k, block) for k in range(16)}
        for t in range(16, 64):
            words[t] = ADD(ADD(ADD(sha256_delta1(words[t-2]),
                                   words[t-7]),
                               sha256_delta0(words[t-15])),
                           words[t-16])

        A = h0
        B = h1
        C = h2
        D = h3
        E = h4
        F = h5
        G = h6
        H = h7
        for t in range(64):
            T1 = ADD(ADD(ADD(ADD(H, sha256_SUM1(E)),
                             sha256_CH(E, F, G)),
                         sha256_k(t)),
                     words[t])
            T2 = ADD(sha256_SUM0(A), sha256_MAJ(A, B, C))
            H = G
            G = F
            F = E
            E = ADD(D, T1)
            D = C
            C = B
            B = A
            A = ADD(T1, T2)

        h0 = ADD(A, h0)
        h1 = ADD(B, h1)
        h2 = ADD(C, h2)        
        h3 = ADD(D, h3)
        h4 = ADD(E, h4)
        h5 = ADD(F, h5)
        h6 = ADD(G, h6)                
        h7 = ADD(H, h7)

    return h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7

def sha384_digest(binary):
    binary = sha384_pad(binary)
    h0 = "1100101110111011100111010101110111000001000001011001111011011000"
    h1 = "0110001010011010001010010010101000110110011111001101010100000111"
    h2 = "1001000101011001000000010101101000110000011100001101110100010111"
    h3 = "0001010100101111111011001101100011110111000011100101100100111001"
    h4 = "0110011100110011001001100110011111111111110000000000101100110001"
    h5 = "1000111010110100010010101000011101101000010110000001010100010001"
    h6 = "1101101100001100001011100000110101100100111110011000111110100111"
    h7 = "0100011110110101010010000001110110111110111110100100111110100100"

    n = len(binary)//1024
    for i in range(n):
        block = word(i, binary, 1024)
        words = {k:word(k, block, 64) for k in range(16)}
        for t in range(16, 80):
            words[t] = ADD(ADD(ADD(sha384_delta1(words[t-2]),
                                   words[t-7], 64),
                               sha384_delta0(words[t-15]), 64),
                           words[t-16], 64)

        A = h0
        B = h1
        C = h2
        D = h3
        E = h4
        F = h5
        G = h6
        H = h7
        for t in range(80):
            T1 = ADD(ADD(ADD(ADD(H, sha384_SUM1(E), 64),
                             sha384_CH(E, F, G), 64),
                         sha384_k(t), 64),
                     words[t], 64)
            T2 = ADD(sha384_SUM0(A), sha384_MAJ(A, B, C), 64)
            H = G
            G = F
            F = E
            E = ADD(D, T1, 64)
            D = C
            C = B
            B = A
            A = ADD(T1, T2, 64)

        h0 = ADD(A, h0, 64)
        h1 = ADD(B, h1, 64)
        h2 = ADD(C, h2, 64)        
        h3 = ADD(D, h3, 64)
        h4 = ADD(E, h4, 64)
        h5 = ADD(F, h5, 64)
        h6 = ADD(G, h6, 64)                
        h7 = ADD(H, h7, 64)

    return h0 + h1 + h2 + h3 + h4 + h5

def sha512_digest(binary):
    binary = sha384_pad(binary)
    h0 = "0110101000001001111001100110011111110011101111001100100100001000"
    h1 = "1011101101100111101011101000010110000100110010101010011100111011"
    h2 = "0011110001101110111100110111001011111110100101001111100000101011"
    h3 = "1010010101001111111101010011101001011111000111010011011011110001"
    h4 = "0101000100001110010100100111111110101101111001101000001011010001"
    h5 = "1001101100000101011010001000110000101011001111100110110000011111"
    h6 = "0001111110000011110110011010101111111011010000011011110101101011"
    h7 = "0101101111100000110011010001100100010011011111100010000101111001"

    n = len(binary)//1024
    for i in range(n):
        block = word(i, binary, 1024)
        words = {k:word(k, block, 64) for k in range(16)}
        for t in range(16, 80):
            words[t] = ADD(ADD(ADD(sha384_delta1(words[t-2]),
                                   words[t-7], 64),
                               sha384_delta0(words[t-15]), 64),
                           words[t-16], 64)

        A = h0
        B = h1
        C = h2
        D = h3
        E = h4
        F = h5
        G = h6
        H = h7
        for t in range(80):
            T1 = ADD(ADD(ADD(ADD(H, sha384_SUM1(E), 64),
                             sha384_CH(E, F, G), 64),
                         sha384_k(t), 64),
                     words[t], 64)
            T2 = ADD(sha384_SUM0(A), sha384_MAJ(A, B, C), 64)
            H = G
            G = F
            F = E
            E = ADD(D, T1, 64)
            D = C
            C = B
            B = A
            A = ADD(T1, T2, 64)

        h0 = ADD(A, h0, 64)
        h1 = ADD(B, h1, 64)
        h2 = ADD(C, h2, 64)        
        h3 = ADD(D, h3, 64)
        h4 = ADD(E, h4, 64)
        h5 = ADD(F, h5, 64)
        h6 = ADD(G, h6, 64)                
        h7 = ADD(H, h7, 64)

    return h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7

###### Main functions ######

def digest(binary, alg):
    if alg == "sha-1":
        return sha1_digest(binary)
    elif alg == "sha-256":
        return sha256_digest(binary)
    elif alg == "sha-384":
        return sha384_digest(binary)    
    elif alg == "sha-512":
        return sha512_digest(binary)
    else:
        raise ValueError(alg+" not supported!")

def hex_digest(binary, alg):
    return bin2hex(digest(binary, alg),
                   (alg == "sha-1" and 40)
                   or (alg == "sha-256" and 64)
                   or (alg == "sha-384" and 96)
                   or (alg == "sha-512" and 128))                   

