Creating a sponge function from MD5

Hello, Habr. On the eve of the start of the " Python Developer. Professional " course, we prepared a translation of the material.






ยซ ยป. , , . , - MD5. , .





- MD5 , , , . , , . - , . , , , .





- MD5, , . MD5, , .





MD5

MD5 โ€“ -, 16 ( 128 ). MD5 , . , , . - - . , Python - hashlib. , . 





In [3]:





md5(b"Test").hex()
md5(b"Test 123").hex()
      
      



Out [3]:





'0cbc6611f5540bd0809a388dc95a615b'
      
      



Out [3]:





'f3957228139a2686632e206478ad1c9e'
      
      



, , . , -. - MD5 . , , , .





โ€“ , ยซยป ยซยป , . , MD5. MD5 16 , 1 , 26 , 5000 , . , MD5 .





. -, , . , โ€“ - . 





( ) . MD5. 16 MD5, MD5.





. , โ€“ , .





  • โ€“ , 0, . 





  • :









    • MD5. 





. , , , , . , :





  • .





  • MD5.





! . MD5, . - , ChaCha20 SHA-512. , , - , .





, , . , . โ€“ .





, , . - MD5 . , MD5 .





, MD5. .





In [5]:





# Initial state
md5(b"").hex()

# Transform once
md5(md5(b"")).hex()
# Transform again
md5(md5(md5(b""))).hex()
# And so on...
      
      



Out [5]:





'd41d8cd98f00b204e9800998ecf8427e'
      
      



Out [5]:





'59adb24ef3cdbe0297f05b395827453f'
      
      



Out [5]:





'8b8154f03b75f58a6c702235bf643629'
      
      



, . Sponge. .





In [6]:





class Sponge(Sponge):
    def transform(self):
        self.state = md5(self.state)
      
      



, , - . MD5, , 16 . , MD5 , , , 16 . , , . .





In [7]:





class Sponge(Sponge):
    def __init__(self):
        self.state = b""
        self.transform()
      
      



, . Sponge, MD5 - d41d8cd98f00b204e9800998ecf8427e







In [8]:





s = Sponge()
s.state.hex()
      
      



Out [8]:





'd41d8cd98f00b204e9800998ecf8427e'
      
      



, . XOR , .





In [9]:





class Sponge(Sponge):
    def absorb_byte(self, byte):
        self.state[0] = byte ^ self.state[0]
        self.transform()
      
      



, . [1,2] [2,1] .





In [10]:





s = Sponge()
s.absorb_byte(1)
s.absorb_byte(2)

s.state.hex()
      
      



Out [10]:





'29a3a137fccfa18e5cfb5054b13aa412'
      
      



In [11]:





s = Sponge()
s.absorb_byte(3)
s.absorb_byte(4)
s.state.hex()
      
      



Out [11]:





'0291c72acd7e7da67bedcb15aa4733c6'
      
      



. . , , .





In [12]:





class Sponge(Sponge):
    def absorb(self, buffer):
        for byte in buffer:
            self.absorb_byte(byte)
      
      



: . , .





In [13]:





s = Sponge()
s.absorb(b"Test")
s.state.hex()
      
      



Out [13]:





'28a7cbf238c85bad13cc0fc4933a68ae'
      
      



, , . , , .





In [14]:





class Sponge(Sponge):
    def squeeze_byte(self):
        byte = self.state[0]
        self.transform()
        return byte
      
      



, , .





In [15]:





s = Sponge()
s.absorb(b"Test")
[s.squeeze_byte() for _ in range(5)]
      
      



Out [15]:





[40, 243, 39, 189, 220]
      
      



โ€“ . , .





In [16]:





class Sponge(Sponge):
    def squeeze(self, size):
        buf = [self.squeeze_byte() for _ in range(size)]
        return bytes(buf)
      
      



In [17]:





s = Sponge()
s.absorb(b"Test")
s.squeeze(5).hex()
      
      



Out [17]:





'28f327bddc'
      
      



, , , . , , 99% . .





, . , , .





โ€“ , . , . , , .





In [18]:





def sponge_hash(data):
    s = Sponge()
    s.absorb(data)
    return s.squeeze(10).hex()

sponge_hash(b"123")
sponge_hash(b"Test 123")
sponge_hash(b"Test 113")
      
      



Out [18]:





'91e292b50acc3c838a0a'
      
      



Out [18]:





'b7a2027b77e56ca5d11f'
      
      



Out [18]:





'62eb28a8017c976f7ccc'
      
      



, -. , . 10 , . , , . .





() . , , . 16- .





In [19]:





import struct

s = Sponge()
s.absorb(b"Seeding the RNG")
def rng():
    buf = s.squeeze(2)
    return struct.unpack('H', buf)[0]
[rng() for _ in range(10)]
      
      



Out [19]:





[29342, 19407, 47040, 9984, 55893, 40500, 56312, 36293, 58610, 10880]
      
      



, . , , . , -, , , . , . , /dev/urandom (https://en.wikipedia.org/wiki//dev/random).





In [20]:





s = Sponge()

with open("/dev/urandom", "rb") as urandom:
    s.absorb(urandom.read(64))
[rng() for _ in range(10)]
      
      



Out [20]:





[56437, 39690, 47308, 16515, 29378, 11318, 32523, 18419, 47972, 4874]
      
      



: , .





, . - . , , - JSON





. , .





In [21]:





def sign(data, key):
    s = Sponge()
    s.absorb(data)
    s.absorb(key)
    return s.squeeze(5)

data = b"Hello world!"
key  = b"password123"
signature = sign(data, key)
signature.hex()
      
      



Out [21]:





'480e4c2b9d'
      
      



. , .





In [22]:





def verify(data, sig, key):
    correct = sign(data, key)
    return sig == correct

verify(data, signature, key)
      
      



Out [22]:





True
      
      



, . .





In [23]:





data = b"Hello wordl!"

verify(data, signature, key)
      
      



Out [23]:





False
      
      



. , , , .





In [24]:





data = b"Hello world!"
signature = bytes.fromhex("481e4c2b9d")

verify(data, signature, key)
      
      



Out [24]:





False
      
      



. , , - , .





In [25]:





def stream_cipher(data, key):
    s = Sponge()
    s.absorb(key)
    
    output = bytearray(data)
    
    for i in range(len(data)):
        key = s.squeeze_byte()
        output[i] ^= key
        
    return output

data = b"Hello, world!"
encrypted = stream_cipher(data, b"password123")
encrypted.hex()
      
      



Out [25]:





'b571d4065c54547bdf1a002d8e'
      
      



, . . .





In [26]:





stream_cipher(encrypted, b"password123")
stream_cipher(encrypted, b"password132")
      
      



Out [26]:





bytearray(b'Hello, world!')
      
      



Out [26]:





bytearray(b'\x12\x88\x98?\x9aESh\x9a\x96\x9d\x17\x1d')
      
      



: , . , . AE AEAD .





: IV/nonce , , . 





, , . 6 ~30 . . :





  1. .





  2. .





  3. , , .





  4. .





  5. , .





In [27]:





import time

key = b"Secret key 123"
def get_otp(key, period=10):
    t = time.time()
    value = int(t / period)
    time_left = period - (t % period)
<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">s</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">=</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">Sponge</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">()</span>
<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">s</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">.</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">absorb</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">key</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">)</span>
<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">s</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">.</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">absorb</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; font-weight: bold;">str</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">value</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">).</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">encode</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(124, 0, 0); font-weight: bold;">'ascii'</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">))</span>

<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">otp</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">=</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254);">[</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">s</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">.</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">squeeze</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 117, 0); font-weight: bold;">1</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">).</span><span style="box-sizing: border-box; font-weight: bold;">hex</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">()</span> <span style="box-sizing: border-box; color: rgb(25, 0, 58); font-weight: bold;">for</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">_</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">in</span> <span style="box-sizing: border-box; font-weight: bold;">range</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 117, 0); font-weight: bold;">3</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">)]</span>
<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">otp</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">=</span> <span style="box-sizing: border-box; color: rgb(124, 0, 0); font-weight: bold;">' '</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">.</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">join</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">otp</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">)</span>

<span style="box-sizing: border-box; color: rgb(25, 0, 58); font-weight: bold;">return</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">otp</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">,</span> <span style="box-sizing: border-box; font-weight: bold;">int</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">time_left</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">)</span>

otp, time_left = get_otp(key)
f"OTP is '{otp}'."
f"Valid for {time_left} more seconds."
      
      



Out [27]:





"OTP is '7c 0b c8'."
      
      



Out [27]:





'Valid for 7 more seconds.'
      
      



, time_left , OTP .





In [28]:





otp == get_otp(key)[0]
      
      



Out [28]:





True
      
      



, , OTP .





In [29]:





time.sleep(time_left + 1)

otp == get_otp(key)[0]
      
      



Out [29]:





False
      
      



: , , . , โ€“ , , , .





, , . , . , .





, , , (nonce), . ยซ ยป.





In [30]:





BLOCKSIZE = 10

def get_block(key, counter):
    s = Sponge()
    s.absorb(key)
    s.absorb(str(counter).encode("ascii"))
    return bytearray(s.squeeze(BLOCKSIZE))
def block_encrypt(data, key):
    size = len(data)
    result = b""
<span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">counter</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">=</span> <span style="box-sizing: border-box; color: rgb(0, 117, 0); font-weight: bold;">0</span>
<span style="box-sizing: border-box; color: rgb(25, 0, 58); font-weight: bold;">while</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">data</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">:</span>
    <span style="box-sizing: border-box; color: rgb(0, 108, 108); font-style: italic;"># Chop off BLOCKSIZE bytes from the data

        data_block = data[:BLOCKSIZE]
        data = data[BLOCKSIZE:]
    <span style="box-sizing: border-box; color: rgb(0, 108, 108); font-style: italic;"># Generate a block cipher block

        block = get_block(key, counter)
    <span style="box-sizing: border-box; color: rgb(25, 0, 58); font-weight: bold;">for</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">i</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">,</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">byte</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">in</span> <span style="box-sizing: border-box; font-weight: bold;">enumerate</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">(</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">data_block</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">):</span>
        <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">block</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">[</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">i</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">]</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">^=</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">byte</span>
    
    <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">result</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">+=</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">block</span>
    <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">counter</span> <span style="box-sizing: border-box; color: rgb(76, 72, 254); font-weight: bold;">+=</span> <span style="box-sizing: border-box; color: rgb(0, 117, 0); font-weight: bold;">1</span>
    
<span style="box-sizing: border-box; color: rgb(25, 0, 58); font-weight: bold;">return</span> <span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">result</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">[:</span><span style="box-sizing: border-box; color: rgb(0, 7, 7); background-color: rgb(243, 255, 255);">size</span><span style="box-sizing: border-box; color: rgb(76, 72, 254);">]</span>

data = b"Hello, world! Don't forget to stay hydrated."
encrypted = block_encrypt(data, b"test")
encrypted.hex()
      
      



Out [30]:





'eec587d16686e81d26ed800677e609a6d2fed11b7a27bbb233370cdba1d941cdc01d42c4c3e7ee90a09333c1'
      
      



, .





In [31]:





block_encrypt(encrypted, b"test")
block_encrypt(encrypted, b"TEST")
      
      



Out [31]:





b"Hello, world! Don't forget to stay hydrated."
      
      



Out [31]:





b'\xd1%\x17\xd9\xe0\x1bh\xaf~2\xc0\x9f\x8da\xb2\xe4\xa4\x05\x99\xc4\x82\xf7\x02\x0c\xed+\xa1\xf4\xefa?\x82l9Q\x05=B>p%\x9e\xa0q'
      
      



, , . . , . , .






"Python Developer. Professional"








All Articles