54
54
# Constants
55
55
PROGRESS_BAR_LENGTH = 60
56
56
57
-
58
57
# update_progress(): Displays or updates a console progress bar
59
58
def update_progress (progress ):
60
59
if PROGRESS :
@@ -94,7 +93,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
94
93
return 1
95
94
96
95
content_size = os .path .getsize (filename )
97
- file_md5 = hashlib .md5 (open (filename , "rb" ).read ()).hexdigest ()
96
+ with open (filename , "rb" ) as f :
97
+ file_md5 = hashlib .md5 (f .read ()).hexdigest ()
98
98
logging .info ("Upload size: %d" , content_size )
99
99
message = "%d %d %d %s\n " % (command , local_port , content_size , file_md5 )
100
100
@@ -118,7 +118,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
118
118
return 1
119
119
sock2 .settimeout (TIMEOUT )
120
120
try :
121
- data = sock2 .recv (37 ).decode ()
121
+ data = sock2 .recv (69 ).decode () # "AUTH " + 64-char SHA256 nonce
122
122
break
123
123
except : # noqa: E722
124
124
sys .stderr .write ("." )
@@ -132,18 +132,32 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
132
132
if data != "OK" :
133
133
if data .startswith ("AUTH" ):
134
134
nonce = data .split ()[1 ]
135
+
136
+ # Generate client nonce (cnonce)
135
137
cnonce_text = "%s%u%s%s" % (filename , content_size , file_md5 , remote_addr )
136
- cnonce = hashlib .md5 (cnonce_text .encode ()).hexdigest ()
137
- passmd5 = hashlib .md5 (password .encode ()).hexdigest ()
138
- result_text = "%s:%s:%s" % (passmd5 , nonce , cnonce )
139
- result = hashlib .md5 (result_text .encode ()).hexdigest ()
138
+ cnonce = hashlib .sha256 (cnonce_text .encode ()).hexdigest ()
139
+
140
+ # PBKDF2-HMAC-SHA256 challenge/response protocol
141
+ # The ESP32 stores the password as SHA256 hash, so we need to hash the password first
142
+ # 1. Hash the password with SHA256 (to match ESP32 storage)
143
+ password_hash = hashlib .sha256 (password .encode ()).hexdigest ()
144
+
145
+ # 2. Derive key using PBKDF2-HMAC-SHA256 with the password hash
146
+ salt = nonce + ":" + cnonce
147
+ derived_key = hashlib .pbkdf2_hmac ('sha256' , password_hash .encode (), salt .encode (), 10000 )
148
+ derived_key_hex = derived_key .hex ()
149
+
150
+ # 3. Create challenge response
151
+ challenge = derived_key_hex + ":" + nonce + ":" + cnonce
152
+ response = hashlib .sha256 (challenge .encode ()).hexdigest ()
153
+
140
154
sys .stderr .write ("Authenticating..." )
141
155
sys .stderr .flush ()
142
- message = "%d %s %s\n " % (AUTH , cnonce , result )
156
+ message = "%d %s %s\n " % (AUTH , cnonce , response )
143
157
sock2 .sendto (message .encode (), remote_address )
144
158
sock2 .settimeout (10 )
145
159
try :
146
- data = sock2 .recv (32 ).decode ()
160
+ data = sock2 .recv (64 ).decode () # SHA256 produces 64 character response
147
161
except : # noqa: E722
148
162
sys .stderr .write ("FAIL\n " )
149
163
logging .error ("No Answer to our Authentication" )
@@ -163,6 +177,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
163
177
sock2 .close ()
164
178
165
179
logging .info ("Waiting for device..." )
180
+
166
181
try :
167
182
sock .settimeout (10 )
168
183
connection , client_address = sock .accept ()
@@ -172,6 +187,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
172
187
logging .error ("No response from device" )
173
188
sock .close ()
174
189
return 1
190
+
175
191
try :
176
192
with open (filename , "rb" ) as f :
177
193
if PROGRESS :
@@ -225,7 +241,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
225
241
logging .error ("Error response from device" )
226
242
connection .close ()
227
243
return 1
228
-
244
+ except Exception as e : # noqa: E722
245
+ logging .error ("Error: %s" , str (e ))
229
246
finally :
230
247
connection .close ()
231
248
0 commit comments