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 โ -, 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')
: IV/nonce , , .
, , . 6 ~30 . . :
.
.
, , .
.
, .
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"