forked from gcagua/ASCON_SEND
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsend.py
More file actions
167 lines (134 loc) · 5.6 KB
/
send.py
File metadata and controls
167 lines (134 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import socket
def ascon_encrypt(key, nonce, associateddata, plaintext, variant="Ascon-128"):
assert variant in ["Ascon-128", "Ascon-128a", "Ascon-80pq"]
if variant in ["Ascon-128", "Ascon-128a"]: assert(len(key) == 16 and len(nonce) == 16)
if variant == "Ascon-80pq": assert(len(key) == 20 and len(nonce) == 16)
S = [0, 0, 0, 0, 0]
k = len(key) * 8 # bits
a = 12 # rounds
b = 8 if variant == "Ascon-128a" else 6 # rounds
rate = 16 if variant == "Ascon-128a" else 8 # bytes
ascon_initialize(S, k, rate, a, b, key, nonce)
ascon_process_associated_data(S, b, rate, associateddata)
ciphertext = ascon_process_plaintext(S, b, rate, plaintext)
tag = ascon_finalize(S, rate, a, key)
return ciphertext + tag
def ascon_initialize(S, k, rate, a, b, key, nonce):
iv_zero_key_nonce = to_bytes([k, rate * 8, a, b]) + zero_bytes(20-len(key)) + key + nonce
S[0], S[1], S[2], S[3], S[4] = bytes_to_state(iv_zero_key_nonce)
ascon_permutation(S, a)
zero_key = bytes_to_state(zero_bytes(40-len(key)) + key)
S[0] ^= zero_key[0]
S[1] ^= zero_key[1]
S[2] ^= zero_key[2]
S[3] ^= zero_key[3]
S[4] ^= zero_key[4]
def ascon_process_associated_data(S, b, rate, associateddata):
if len(associateddata) > 0:
a_padding = to_bytes([0x80]) + zero_bytes(rate - (len(associateddata) % rate) - 1)
a_padded = associateddata + a_padding
for block in range(0, len(a_padded), rate):
S[0] ^= bytes_to_int(a_padded[block:block+8])
if rate == 16:
S[1] ^= bytes_to_int(a_padded[block+8:block+16])
ascon_permutation(S, b)
S[4] ^= 1
def ascon_process_plaintext(S, b, rate, plaintext):
p_lastlen = len(plaintext) % rate
p_padding = to_bytes([0x80]) + zero_bytes(rate-p_lastlen-1)
p_padded = plaintext + p_padding
# first t-1 blocks
ciphertext = to_bytes([])
for block in range(0, len(p_padded) - rate, rate):
if rate == 8:
S[0] ^= bytes_to_int(p_padded[block:block+8])
ciphertext += int_to_bytes(S[0], 8)
elif rate == 16:
S[0] ^= bytes_to_int(p_padded[block:block+8])
S[1] ^= bytes_to_int(p_padded[block+8:block+16])
ciphertext += (int_to_bytes(S[0], 8) + int_to_bytes(S[1], 8))
ascon_permutation(S, b)
# last block t
block = len(p_padded) - rate
if rate == 8:
S[0] ^= bytes_to_int(p_padded[block:block+8])
ciphertext += int_to_bytes(S[0], 8)[:p_lastlen]
elif rate == 16:
S[0] ^= bytes_to_int(p_padded[block:block+8])
S[1] ^= bytes_to_int(p_padded[block+8:block+16])
ciphertext += (int_to_bytes(S[0], 8)[:min(8,p_lastlen)] + int_to_bytes(S[1], 8)[:max(0,p_lastlen-8)])
return ciphertext
def ascon_finalize(S, rate, a, key):
assert(len(key) in [16,20])
S[rate//8+0] ^= bytes_to_int(key[0:8])
S[rate//8+1] ^= bytes_to_int(key[8:16])
S[rate//8+2] ^= bytes_to_int(key[16:] + zero_bytes(24-len(key)))
ascon_permutation(S, a)
S[3] ^= bytes_to_int(key[-16:-8])
S[4] ^= bytes_to_int(key[-8:])
tag = int_to_bytes(S[3], 8) + int_to_bytes(S[4], 8)
return tag
def ascon_permutation(S, rounds=1):
assert(rounds <= 12)
for r in range(12-rounds, 12):
# --- add round constants ---
S[2] ^= (0xf0 - r*0x10 + r*0x1)
# --- substitution layer ---
S[0] ^= S[4]
S[4] ^= S[3]
S[2] ^= S[1]
T = [(S[i] ^ 0xFFFFFFFFFFFFFFFF) & S[(i+1)%5] for i in range(5)]
for i in range(5):
S[i] ^= T[(i+1)%5]
S[1] ^= S[0]
S[0] ^= S[4]
S[3] ^= S[2]
S[2] ^= 0XFFFFFFFFFFFFFFFF
# --- linear diffusion layer ---
S[0] ^= rotr(S[0], 19) ^ rotr(S[0], 28)
S[1] ^= rotr(S[1], 61) ^ rotr(S[1], 39)
S[2] ^= rotr(S[2], 1) ^ rotr(S[2], 6)
S[3] ^= rotr(S[3], 10) ^ rotr(S[3], 17)
S[4] ^= rotr(S[4], 7) ^ rotr(S[4], 41)
def demo_print(data):
maxlen = max([len(text) for (text, val) in data])
for text, val in data:
print("{text}:{align} 0x{val} ({length} bytes)".format(text=text, align=((maxlen - len(text)) * " "), val=bytes_to_hex(val), length=len(val)))
def bytes_to_hex(b):
return b.hex()
#return "".join(x.encode('hex') for x in b)
def get_random_bytes(num):
import os
return to_bytes(os.urandom(num))
def zero_bytes(n):
return n * b"\x00"
def to_bytes(l): # where l is a list or bytearray or bytes
return bytes(bytearray(l))
def bytes_to_int(bytes):
return sum([bi << ((len(bytes) - 1 - i)*8) for i, bi in enumerate(to_bytes(bytes))])
def bytes_to_state(bytes):
return [bytes_to_int(bytes[8*w:8*(w+1)]) for w in range(5)]
def int_to_bytes(integer, nbytes):
return to_bytes([(integer >> ((nbytes - 1 - i) * 8)) % 256 for i in range(nbytes)])
def rotr(val, r):
return (val >> r) | ((val & (1<<r)-1) << (64-r))
host = "192.168.0.12"
port = 5000
s = socket.socket()
s.connect((host, port))
print("Connected to: ", host)
key = get_random_bytes(16) # zero_bytes(keysize)
nonce = get_random_bytes(16) # zero_bytes(16)
s.send(key)
print(key)
s.send(nonce)
print(nonce)
associated_data = input("Enter the associated data: ")
associateddata = associated_data.encode('utf-8')
s.send(associateddata)
print(associateddata)
plain_text = input("Enter the plain text: ")
plaintext = plain_text.encode('utf-8')
ciphertext = ascon_encrypt(key, nonce, associateddata, plaintext, "Ascon-128")
s.send(ciphertext)
print(ciphertext)