@@ -110,14 +110,18 @@ def smp_step_2(user_data: dict, user_data_lock, contact_id: str, message: dict,
110110
111111 signing_private_key , signing_public_key = generate_sign_keys ()
112112
113- our_nonce = secrets .token_bytes (SMP_NONCE_LENGTH )
113+ our_nonce = sha3_512 ( secrets .token_bytes (SMP_NONCE_LENGTH ))[: SMP_NONCE_LENGTH ]
114114
115115 key_ciphertext , chacha_key = encap_shared_secret (contact_kem_public_key , ML_KEM_1024_NAME )
116116 chacha_key = sha3_512 (chacha_key )[:32 ]
117117
118+ our_next_strand_nonce = sha3_512 (secrets .token_bytes (XCHACHA20POLY1305_NONCE_LEN ))[:XCHACHA20POLY1305_NONCE_LEN ]
119+ contact_next_strand_nonce = sha3_512 (secrets .token_bytes (XCHACHA20POLY1305_NONCE_LEN ))[:XCHACHA20POLY1305_NONCE_LEN ]
120+
121+
118122 ciphertext_nonce , ciphertext_blob = encrypt_xchacha20poly1305 (
119123 chacha_key ,
120- signing_public_key + our_nonce ,
124+ signing_public_key + our_nonce + our_next_strand_nonce + contact_next_strand_nonce ,
121125 counter = 2
122126 )
123127
@@ -146,6 +150,9 @@ def smp_step_2(user_data: dict, user_data_lock, contact_id: str, message: dict,
146150 user_data ["contacts" ][contact_id ]["lt_sign_keys" ]["our_keys" ]["public_key" ] = signing_public_key
147151
148152 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["smp_step" ] = 4
153+
154+ user_data ["contacts" ][contact_id ]["our_next_strand_nonce" ] = our_next_strand_nonce
155+ user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ] = contact_next_strand_nonce
149156
150157
151158def smp_step_3 (user_data : dict , user_data_lock : threading .Lock , contact_id : str , message : dict , ui_queue : queue .Queue ()) -> None :
@@ -173,9 +180,12 @@ def smp_step_3(user_data: dict, user_data_lock: threading.Lock, contact_id: str,
173180 )
174181
175182 contact_signing_public_key = smp_plaintext [:ML_DSA_87_PK_LEN ]
176- contact_nonce = smp_plaintext [ML_DSA_87_PK_LEN :]
183+ contact_nonce = smp_plaintext [ML_DSA_87_PK_LEN : ML_DSA_87_PK_LEN + SMP_NONCE_LENGTH ]
177184
178- our_nonce = secrets .token_bytes (SMP_NONCE_LENGTH )
185+ contact_next_strand_nonce = smp_plaintext [ML_DSA_87_PK_LEN + SMP_NONCE_LENGTH : ML_DSA_87_PK_LEN + SMP_NONCE_LENGTH + XCHACHA20POLY1305_NONCE_LEN ]
186+ our_next_strand_nonce = smp_plaintext [ML_DSA_87_PK_LEN + SMP_NONCE_LENGTH + XCHACHA20POLY1305_NONCE_LEN :]
187+
188+ our_nonce = sha3_512 (secrets .token_bytes (SMP_NONCE_LENGTH ))[:SMP_NONCE_LENGTH ]
179189
180190 signing_private_key , signing_public_key = generate_sign_keys ()
181191
@@ -190,17 +200,19 @@ def smp_step_3(user_data: dict, user_data_lock: threading.Lock, contact_id: str,
190200 our_proof = hmac .new (answer_secret , our_proof , hashlib .sha3_512 ).digest ()
191201
192202 logger .debug ("Our proof of contact (%s) public-key fingerprint: %s" , contact_id , our_proof )
203+
193204
194- ciphertext_nonce , ciphertext_blob = encrypt_xchacha20poly1305 (
205+ our_new_strand_nonce = sha3_512 (secrets .token_bytes (XCHACHA20POLY1305_NONCE_LEN ))[:XCHACHA20POLY1305_NONCE_LEN ]
206+ _ , ciphertext_blob = encrypt_xchacha20poly1305 (
195207 chacha_key ,
196- signing_public_key + our_nonce + our_proof + question .encode ("utf-8" ),
197- counter = 3
208+ our_new_strand_nonce + signing_public_key + our_nonce + our_proof + question .encode ("utf-8" ),
209+ nonce = our_next_strand_nonce
198210 )
199211
200212
201213 try :
202214 http_request (f"{ server_url } /smp/step" , "POST" , payload = {
203- "ciphertext_blob" : b64encode (ciphertext_nonce + ciphertext_blob ).decode (),
215+ "ciphertext_blob" : b64encode (ciphertext_blob ).decode (),
204216 "recipient" : contact_id
205217
206218 }, auth_token = auth_token )
@@ -214,34 +226,51 @@ def smp_step_3(user_data: dict, user_data_lock: threading.Lock, contact_id: str,
214226 with user_data_lock :
215227 user_data ["contacts" ][contact_id ]["lt_sign_keys" ]["contact_public_key" ] = contact_signing_public_key
216228
217- user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["contact_nonce" ] = b64encode (contact_nonce ).decode ()
218- user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["our_nonce" ] = b64encode (our_nonce ).decode ()
219- user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_key" ] = b64encode (chacha_key ).decode ()
229+ user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["contact_nonce" ] = b64encode (contact_nonce ).decode ()
230+ user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["our_nonce" ] = b64encode (our_nonce ).decode ()
231+ user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_key" ] = b64encode (chacha_key ).decode ()
220232
221233 user_data ["contacts" ][contact_id ]["lt_sign_keys" ]["our_keys" ]["private_key" ] = signing_private_key
222234 user_data ["contacts" ][contact_id ]["lt_sign_keys" ]["our_keys" ]["public_key" ] = signing_public_key
223235
236+ user_data ["contacts" ][contact_id ]["our_next_strand_nonce" ] = our_new_strand_nonce
237+ user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ] = contact_next_strand_nonce
238+
239+
224240 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["smp_step" ] = 5
225241
226242
227243def smp_step_4_request_answer (user_data , user_data_lock , contact_id , message , ui_queue ) -> None :
228244 with user_data_lock :
229245 tmp_key = b64decode (user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_key" ])
230246
247+ our_next_strand_nonce = user_data ["contacts" ][contact_id ]["our_next_strand_nonce" ]
248+ contact_next_strand_nonce = user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ]
249+
250+
231251 ciphertext_blob = b64decode (message ["ciphertext_blob" ], validate = True )
232- smp_plaintext = decrypt_xchacha20poly1305 (tmp_key , ciphertext_blob [:XCHACHA20POLY1305_NONCE_LEN ], ciphertext_blob [XCHACHA20POLY1305_NONCE_LEN :])
252+
253+
254+ smp_plaintext = decrypt_xchacha20poly1305 (tmp_key , contact_next_strand_nonce , ciphertext_blob )
255+
256+ contact_new_strand_nonce = smp_plaintext [:XCHACHA20POLY1305_NONCE_LEN ]
233257
234- contact_signing_public_key = smp_plaintext [:ML_DSA_87_PK_LEN ]
235- contact_nonce = b64encode (smp_plaintext [ML_DSA_87_PK_LEN : SMP_NONCE_LENGTH + ML_DSA_87_PK_LEN ]).decode ()
236- contact_proof = b64encode (smp_plaintext [SMP_NONCE_LENGTH + ML_DSA_87_PK_LEN : SMP_NONCE_LENGTH + SMP_PROOF_LENGTH + ML_DSA_87_PK_LEN ]).decode ()
237- question = smp_plaintext [SMP_NONCE_LENGTH + SMP_PROOF_LENGTH + ML_DSA_87_PK_LEN :].decode ("utf-8" )
258+ contact_signing_public_key = smp_plaintext [XCHACHA20POLY1305_NONCE_LEN : ML_DSA_87_PK_LEN + XCHACHA20POLY1305_NONCE_LEN ]
259+
260+ contact_nonce = b64encode (smp_plaintext [XCHACHA20POLY1305_NONCE_LEN + ML_DSA_87_PK_LEN : SMP_NONCE_LENGTH + ML_DSA_87_PK_LEN + XCHACHA20POLY1305_NONCE_LEN ]).decode ()
261+
262+ contact_proof = b64encode (smp_plaintext [XCHACHA20POLY1305_NONCE_LEN + SMP_NONCE_LENGTH + ML_DSA_87_PK_LEN : SMP_NONCE_LENGTH + SMP_PROOF_LENGTH + ML_DSA_87_PK_LEN + XCHACHA20POLY1305_NONCE_LEN ]).decode ()
263+
264+ question = smp_plaintext [SMP_NONCE_LENGTH + XCHACHA20POLY1305_NONCE_LEN + SMP_PROOF_LENGTH + ML_DSA_87_PK_LEN :].decode ("utf-8" )
265+
238266
239267
240268 with user_data_lock :
241269 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["question" ] = question
242270 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_proof" ] = contact_proof
243271 # user_data["contacts"][contact_id]["lt_sign_key_smp"]["smp_step"] = 5
244272
273+ user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ] = contact_new_strand_nonce
245274
246275 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["contact_nonce" ] = contact_nonce
247276
@@ -269,6 +298,8 @@ def smp_step_4_answer_provided(user_data, user_data_lock, contact_id, answer, ui
269298
270299 our_signing_public_key = user_data ["contacts" ][contact_id ]["lt_sign_keys" ]["our_keys" ]["public_key" ]
271300
301+ our_next_strand_nonce = user_data ["contacts" ][contact_id ]["our_next_strand_nonce" ]
302+
272303 tmp_key = b64decode (user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_key" ])
273304
274305 answer = normalize_answer (answer )
@@ -286,10 +317,11 @@ def smp_step_4_answer_provided(user_data, user_data_lock, contact_id, answer, ui
286317 logger .debug ("SMP Proof sent to us: %s" , contact_proof )
287318 logger .debug ("Our compute message: %s" , our_proof )
288319
320+
289321 # Verify Contact's version of our public-key fingerprint matches our actual public-key fingerprint
290322 # We compare using compare_digest to prevent timing analysis by avoiding content-based short circuiting behaviour
291323 if not hmac .compare_digest (our_proof , contact_proof ):
292- logger .warning ("SMP Verification failed" )
324+ logger .warning ("SMP Verification failed at step 4 " )
293325 smp_failure_notify_contact (user_data , user_data_lock , contact_id , ui_queue )
294326 return
295327
@@ -300,19 +332,21 @@ def smp_step_4_answer_provided(user_data, user_data_lock, contact_id, answer, ui
300332 our_proof = contact_nonce + our_nonce + contact_key_fingerprint
301333 our_proof = hmac .new (answer_secret , our_proof , hashlib .sha3_512 ).digest ()
302334
303- our_strand_key = secrets .token_bytes (32 )
304- contact_strand_key = secrets .token_bytes (32 )
305335
306- ciphertext_nonce , ciphertext_blob = encrypt_xchacha20poly1305 (
336+ our_strand_key = sha3_512 (secrets .token_bytes (32 ))[:32 ]
337+ contact_strand_key = sha3_512 (secrets .token_bytes (32 ))[:32 ]
338+
339+ our_new_strand_nonce = sha3_512 (secrets .token_bytes (XCHACHA20POLY1305_NONCE_LEN ))[:XCHACHA20POLY1305_NONCE_LEN ]
340+ _ , ciphertext_blob = encrypt_xchacha20poly1305 (
307341 tmp_key ,
308- our_proof + our_strand_key + contact_strand_key ,
309- counter = 4
342+ our_new_strand_nonce + our_proof + our_strand_key + contact_strand_key ,
343+ nonce = our_next_strand_nonce
310344 )
311345
312346
313347 try :
314348 http_request (f"{ server_url } /smp/step" , "POST" , payload = {
315- "ciphertext_blob" : b64encode (ciphertext_nonce + ciphertext_blob ).decode (),
349+ "ciphertext_blob" : b64encode (ciphertext_blob ).decode (),
316350 "recipient" : contact_id
317351 }, auth_token = auth_token )
318352 except Exception :
@@ -326,10 +360,11 @@ def smp_step_4_answer_provided(user_data, user_data_lock, contact_id, answer, ui
326360
327361 with user_data_lock :
328362 user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["answer" ] = answer
329- user_data ["contacts" ][contact_id ]["our_strand_key" ] = our_strand_key
330- user_data ["contacts" ][contact_id ]["contact_strand_key" ] = contact_strand_key
331363
364+ user_data ["contacts" ][contact_id ]["our_next_strand_nonce" ] = our_new_strand_nonce
332365
366+ user_data ["contacts" ][contact_id ]["our_strand_key" ] = our_strand_key
367+ user_data ["contacts" ][contact_id ]["contact_strand_key" ] = contact_strand_key
333368
334369
335370
@@ -347,7 +382,8 @@ def smp_step_5(user_data, user_data_lock, contact_id, message, ui_queue) -> None
347382 contact_nonce = b64decode (user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["contact_nonce" ], validate = True )
348383
349384 tmp_key = b64decode (user_data ["contacts" ][contact_id ]["lt_sign_key_smp" ]["tmp_key" ])
350-
385+ contact_next_strand_nonce = user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ]
386+
351387
352388 our_key_fingerprint = sha3_512 (our_signing_public_key + our_kem_public_key )
353389
@@ -360,25 +396,32 @@ def smp_step_5(user_data, user_data_lock, contact_id, message, ui_queue) -> None
360396 our_proof = hmac .new (answer_secret , our_proof , hashlib .sha3_512 ).digest ()
361397
362398 ciphertext_blob = b64decode (message ["ciphertext_blob" ], validate = True )
363- smp_plaintext = decrypt_xchacha20poly1305 (tmp_key , ciphertext_blob [:XCHACHA20POLY1305_NONCE_LEN ], ciphertext_blob [XCHACHA20POLY1305_NONCE_LEN :])
364399
365- contact_proof = smp_plaintext [:SMP_PROOF_LENGTH ]
366- contact_strand_key = smp_plaintext [SMP_PROOF_LENGTH : SMP_PROOF_LENGTH + 32 ]
367- our_strand_key = smp_plaintext [SMP_PROOF_LENGTH + 32 :]
400+
401+ smp_plaintext = decrypt_xchacha20poly1305 (tmp_key , contact_next_strand_nonce , ciphertext_blob )
368402
403+ contact_new_strand_nonce = smp_plaintext [:XCHACHA20POLY1305_NONCE_LEN ]
404+
405+ contact_proof = smp_plaintext [XCHACHA20POLY1305_NONCE_LEN : SMP_PROOF_LENGTH + XCHACHA20POLY1305_NONCE_LEN ]
406+
407+ contact_strand_key = smp_plaintext [XCHACHA20POLY1305_NONCE_LEN + SMP_PROOF_LENGTH : XCHACHA20POLY1305_NONCE_LEN + SMP_PROOF_LENGTH + 32 ]
408+ our_strand_key = smp_plaintext [XCHACHA20POLY1305_NONCE_LEN + SMP_PROOF_LENGTH + 32 :]
369409
370410 logger .debug ("SMP Proof sent to us: %s" , contact_proof )
371411 logger .debug ("Our compute message: %s" , our_proof )
372412
373413
414+
374415 # Verify Contact's version of our public-key fingerprint matches our actual public-key fingerprint
375416 # We compare using compare_digest to prevent timing analysis by avoiding content-based short circuiting behaviour
376417 if not hmac .compare_digest (our_proof , contact_proof ):
377- logger .warning ("SMP Verification failed" )
418+ logger .warning ("SMP Verification failed at step 5 " )
378419 smp_failure_notify_contact (user_data , user_data_lock , contact_id , ui_queue )
379420 return
380421
381422 with user_data_lock :
423+ user_data ["contacts" ][contact_id ]["contact_next_strand_nonce" ] = contact_new_strand_nonce
424+
382425 user_data ["contacts" ][contact_id ]["our_strand_key" ] = our_strand_key
383426 user_data ["contacts" ][contact_id ]["contact_strand_key" ] = contact_strand_key
384427
@@ -480,6 +523,12 @@ def smp_data_handler(user_data, user_data_lock, user_data_copied, ui_queue, mess
480523 except Exception :
481524 smp_step = 2
482525
526+
527+ if "failure" in message :
528+ # Delete SMP state for contact
529+ smp_failure (user_data , user_data_lock , contact_id , ui_queue )
530+ return
531+
483532 # Check if we don't have this contact saved
484533 if contact_id not in user_data_copied ["contacts" ]:
485534 # We assume it has to be step 1 because the contact did not exist before
@@ -508,10 +557,6 @@ def smp_data_handler(user_data, user_data_lock, user_data_copied, ui_queue, mess
508557 elif smp_step == 2 :
509558 smp_step_2 (user_data , user_data_lock , contact_id , message , ui_queue )
510559
511- elif "failure" in message :
512- # Delete SMP state for contact
513- smp_failure (user_data , user_data_lock , contact_id , ui_queue )
514-
515560 elif smp_step == 3 :
516561 if (not user_data_copied ["contacts" ][contact_id ]["lt_sign_key_smp" ]["pending_verification" ]):
517562 logger .error ("Contact (%s) is not pending verification, yet they sent us a SMP request. Ignoring it." , contact_id )
0 commit comments