-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.py
174 lines (135 loc) · 6.15 KB
/
client.py
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
168
169
170
171
172
import socket
import sys
import random
import secrets
import time
from pprint import pprint
import scrypt
import hashlib
from Crypto.Cipher import AES
from settings import Settings
settings = Settings()
# client class
class Client():
# init client (Set properties and connect to server)
def __init__(self):
# set local properties and measurement variables
self.N_requests = 0
self.oram_time_elapsed = 0
self.oram = None
self.server = None
# load ORAM
if settings.oram == 'sqrt':
import oram.sqrt
self.oram = oram.sqrt.Sqrt(self)
elif settings.oram == 'pathoram':
import oram.pathoram
self.oram = oram.pathoram.Pathoram(self)
elif settings.oram == 'trivial':
import oram.trivial
self.oram = oram.trivial.Trivial(self)
# calculate 128-bit AES encryption key
self.encryption_key = scrypt.hash(settings.encryption_password, hashlib.sha256(settings.encryption_password.encode()).hexdigest(), 16384, 8, 1, 16)
# init server entry?
if settings.skip_network_connection:
import server
self.server = server.Server()
def reset_database(self):
# reset server database
if self.server != None:
self.server.create_database()
# padds specific content with random bytes until it has the size of a block
def padd_content(self, content):
# length is already blockSize
if len(content) == settings.blockSize:
return content
missing_bytes = settings.blockSize - len(content)
return content + bytearray(secrets.token_bytes(missing_bytes))
# generate dummy bytes with length blockSize
def generate_dummy_block(self):
return bytearray(secrets.token_bytes(settings.blockSize))
def write(self, block_id, data):
# no ORAM method is set, this means that there is no need to really store it to the server
# this method is used to test the efficiency of the file conversion method, without the delay of communication between client+server
if self.oram == None:
self.N_requests += 1
return
# calculate duration of ORAM access
tmp_time = time.time()
result = self.oram.access(block_id, data)
tmp_time = time.time()-tmp_time
self.oram_time_elapsed += tmp_time
return result
def read(self, block_id):
# no ORAM method is set, this means that there is no need to really store it to the server
# this method is used to test the efficiency of the file conversion method, without the delay of communication between client+server
if self.oram == None:
self.N_requests += 1
return
# calculate duration of ORAM access
tmp_time = time.time()
result = self.oram.access(block_id)
tmp_time = time.time()-tmp_time
self.oram_time_elapsed += tmp_time
return result
# send message and receive response
# command = "R" or "W"
def _send(self, command, server_block_id, data, client_block_id=None):
# increment requests (called by ORAM scripts)
self.N_requests += 1
# encode client_block_id in data?
client_block_encoding_length = 0
if settings.oram == 'trivial' or settings.oram == 'pathoram':
# need dummy client block (=0)
if client_block_id == None:
client_block_id = 0
# get encoding length
client_block_encoding_length = len(str(settings.N_blocks))
# padd client_block_id string
client_block_id = str(client_block_id)
client_block_id = "0"*(client_block_encoding_length-len(client_block_id)) + client_block_id
# append to data
data += client_block_id.encode()
# if command == 'W' and int(client_block_id) != 0:
# pprint("Write block " + str(client_block_id) + " to server address " + str(server_block_id))
# encrypt data with AES(-128)
aes_enc = AES.new(self.encryption_key, AES.MODE_CTR)
data = aes_enc.encrypt(data)
enc_nonce = aes_enc.nonce
## send data to the correct block
received = None
# communicate over network
if settings.skip_network_connection == False:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((settings.HOST, settings.PORT))
# send command and data
pprint("Send command: " + str(command) + str(server_block_id))
self.socket.send(bytes(command + str(server_block_id) + "\n", "utf-8"))
pprint("Send data")
self.socket.sendall(enc_nonce+data)
# receive data
pprint("receive data")
received = bytearray(self.socket.recv(settings.blockSize+settings.encryption_nonce_length+client_block_encoding_length))
self.socket.close()
# communicate using code
else:
received = self.server.handle_request(bytes(command + str(server_block_id), "utf-8"), enc_nonce+data)
# decrypt received with AES
dec_nonce = received[:settings.encryption_nonce_length]
received = received[settings.encryption_nonce_length:]
aes_dec = AES.new(self.encryption_key, AES.MODE_CTR, nonce=dec_nonce)
received = aes_dec.decrypt(received)
# if client_block_id != None: split data and client_block_id
if client_block_id != None:
received_block_id = 0
if len(received) > settings.blockSize:
try:
received_block_id = int(received[settings.blockSize:].decode())
except (UnicodeDecodeError, ValueError): # server-generated random bytes, cannot be decoded
received_block_id = 0
received = received[:settings.blockSize]
# if command == 'R' and int(received_block_id) != 0:
# pprint("Read block " + str(received_block_id) + " from server address " + str(server_block_id))
return(received, received_block_id)
# return result
return received