5454# Constants
5555PROGRESS_BAR_LENGTH = 60
5656
57-
5857# update_progress(): Displays or updates a console progress bar
5958def update_progress (progress ):
6059 if PROGRESS :
@@ -94,7 +93,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
9493 return 1
9594
9695 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 ()
9898 logging .info ("Upload size: %d" , content_size )
9999 message = "%d %d %d %s\n " % (command , local_port , content_size , file_md5 )
100100
@@ -118,7 +118,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
118118 return 1
119119 sock2 .settimeout (TIMEOUT )
120120 try :
121- data = sock2 .recv (37 ).decode ()
121+ data = sock2 .recv (69 ).decode () # "AUTH " + 64-char SHA256 nonce
122122 break
123123 except : # noqa: E722
124124 sys .stderr .write ("." )
@@ -132,18 +132,32 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
132132 if data != "OK" :
133133 if data .startswith ("AUTH" ):
134134 nonce = data .split ()[1 ]
135+
136+ # Generate client nonce (cnonce)
135137 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+
140154 sys .stderr .write ("Authenticating..." )
141155 sys .stderr .flush ()
142- message = "%d %s %s\n " % (AUTH , cnonce , result )
156+ message = "%d %s %s\n " % (AUTH , cnonce , response )
143157 sock2 .sendto (message .encode (), remote_address )
144158 sock2 .settimeout (10 )
145159 try :
146- data = sock2 .recv (32 ).decode ()
160+ data = sock2 .recv (64 ).decode () # SHA256 produces 64 character response
147161 except : # noqa: E722
148162 sys .stderr .write ("FAIL\n " )
149163 logging .error ("No Answer to our Authentication" )
@@ -163,6 +177,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
163177 sock2 .close ()
164178
165179 logging .info ("Waiting for device..." )
180+
166181 try :
167182 sock .settimeout (10 )
168183 connection , client_address = sock .accept ()
@@ -172,6 +187,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
172187 logging .error ("No response from device" )
173188 sock .close ()
174189 return 1
190+
175191 try :
176192 with open (filename , "rb" ) as f :
177193 if PROGRESS :
@@ -225,7 +241,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename,
225241 logging .error ("Error response from device" )
226242 connection .close ()
227243 return 1
228-
244+ except Exception as e : # noqa: E722
245+ logging .error ("Error: %s" , str (e ))
229246 finally :
230247 connection .close ()
231248
0 commit comments