Skip to content

Commit d33d0b6

Browse files
committed
refactor: update to latest STRANDLOCK
1 parent 3b4ddfe commit d33d0b6

File tree

4 files changed

+25
-60
lines changed

4 files changed

+25
-60
lines changed

core/constants.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
SMP_QUESTION_MAX_LEN = 512
3939

4040
KEYS_HASH_CHAIN_LEN = 64
41-
MESSAGE_HASH_CHAIN_LEN = 64
42-
4341

4442

4543
# NIST-specified key sizes (bytes) and metadata

logic/contacts.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,7 @@ def save_contact(user_data: dict, user_data_lock, contact_id: str) -> None:
9090
"contact_strand_key": None,
9191
"our_next_strand_nonce": None,
9292
"contact_next_strand_nonce": None,
93-
"our_pads": {
94-
"hash_chain": None,
95-
"pads": None
96-
},
97-
"contact_pads": {
98-
"hash_chain": None,
99-
"pads": None
100-
},
93+
"our_pads": None,
94+
"contact_pads": None
10195
}
10296

logic/message.py

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
)
3030
from core.constants import (
3131
MSG_TYPE,
32-
MESSAGE_HASH_CHAIN_LEN,
3332
OTP_MAX_BUCKET,
3433
OTP_PAD_SIZE,
3534
ML_KEM_1024_NAME,
@@ -78,12 +77,10 @@ def generate_and_send_pads(user_data, user_data_lock, contact_id: str, ui_queue)
7877

7978
otp_batch_signature = create_signature(ML_DSA_87_NAME, kyber_ciphertext_blob + mceliece_ciphertext_blob, our_lt_private_key)
8079

81-
hash_chain_seed = sha3_512(secrets.token_bytes(MESSAGE_HASH_CHAIN_LEN))
82-
8380
our_new_strand_nonce = sha3_512(secrets.token_bytes(XCHACHA20POLY1305_NONCE_LEN))[:XCHACHA20POLY1305_NONCE_LEN]
8481
_, ciphertext_blob = encrypt_xchacha20poly1305(
8582
our_strand_key,
86-
MSG_TYPE + b"\x00" + our_new_strand_nonce + hash_chain_seed + otp_batch_signature + kyber_ciphertext_blob + mceliece_ciphertext_blob + xchacha_shared_secrets,
83+
MSG_TYPE + b"\x00" + our_new_strand_nonce + otp_batch_signature + kyber_ciphertext_blob + mceliece_ciphertext_blob + xchacha_shared_secrets,
8784
nonce = our_next_strand_nonce
8885
)
8986

@@ -109,9 +106,8 @@ def generate_and_send_pads(user_data, user_data_lock, contact_id: str, ui_queue)
109106
with user_data_lock:
110107
user_data["contacts"][contact_id]["our_next_strand_nonce"] = our_new_strand_nonce
111108
user_data["contacts"][contact_id]["our_strand_key"] = our_strand_key
112-
user_data["contacts"][contact_id]["our_pads"]["pads"] = pads[32:]
109+
user_data["contacts"][contact_id]["our_pads"] = pads[32:]
113110

114-
user_data["contacts"][contact_id]["our_pads"]["hash_chain"] = hash_chain_seed
115111

116112
save_account_data(user_data, user_data_lock)
117113

@@ -137,7 +133,7 @@ def send_message_processor(user_data, user_data_lock, contact_id: str, message:
137133
contact_kyber_public_key = user_data["contacts"][contact_id]["ephemeral_keys"]["contact_public_keys"][ML_KEM_1024_NAME]
138134
contact_mceliece_public_key = user_data["contacts"][contact_id]["ephemeral_keys"]["contact_public_keys"][CLASSIC_MCELIECE_8_F_NAME]
139135

140-
our_pads = user_data["contacts"][contact_id]["our_pads"]["pads"]
136+
our_pads = user_data["contacts"][contact_id]["our_pads"]
141137

142138

143139

@@ -162,10 +158,7 @@ def send_message_processor(user_data, user_data_lock, contact_id: str, message:
162158

163159

164160
with user_data_lock:
165-
our_pads = user_data["contacts"][contact_id]["our_pads"]["pads"]
166-
167-
with user_data_lock:
168-
our_hash_chain = user_data["contacts"][contact_id]["our_pads"]["hash_chain"]
161+
our_pads = user_data["contacts"][contact_id]["our_pads"]
169162

170163

171164

@@ -182,29 +175,26 @@ def send_message_processor(user_data, user_data_lock, contact_id: str, message:
182175
logger.debug("Our old pad size is %d and new size after the message is %d", len(our_pads), len(new_pads))
183176
break
184177
except ValueError as e:
185-
logger.debug("Failed to encrypt message to contact (%s) with error: %s", contact_id, str(e))
178+
logger.debug("Couldnt encrypt message to contact (%s) with error: %s", contact_id, str(e))
186179
logger.info("Your message size (%d) when padded, is larger than our pads size (%s), therefore we are generating new pads for you", len(message), len(our_pads))
187180

188181
if not generate_and_send_pads(user_data, user_data_lock, contact_id, ui_queue):
189182
return False
190183

191184
with user_data_lock:
192-
our_pads = user_data["contacts"][contact_id]["our_pads"]["pads"]
193-
our_hash_chain = user_data["contacts"][contact_id]["our_pads"]["hash_chain"]
185+
our_pads = user_data["contacts"][contact_id]["our_pads"]
194186

195187

196188

197189
# Unlike in other functions, we truncate pads here and compute the next hash chain regardless of request being successful or not
198190
# because a malicious server could make our requests fail to force us to re-use the same pad for our next message
199191
# which would break all of our security
200192

201-
next_hash_chain = sha3_512(our_hash_chain + message_encrypted)
202193

203194
our_new_strand_nonce = sha3_512(secrets.token_bytes(XCHACHA20POLY1305_NONCE_LEN))[:XCHACHA20POLY1305_NONCE_LEN]
204195

205196
with user_data_lock:
206-
user_data["contacts"][contact_id]["our_pads"]["pads"] = user_data["contacts"][contact_id]["our_pads"]["pads"][len(message_encrypted):]
207-
user_data["contacts"][contact_id]["our_pads"]["hash_chain"] = next_hash_chain
197+
user_data["contacts"][contact_id]["our_pads"] = user_data["contacts"][contact_id]["our_pads"][len(message_encrypted):]
208198

209199
our_strand_key = user_data["contacts"][contact_id]["our_strand_key"]
210200

@@ -215,7 +205,7 @@ def send_message_processor(user_data, user_data_lock, contact_id: str, message:
215205

216206
_, ciphertext_blob = encrypt_xchacha20poly1305(
217207
our_strand_key,
218-
MSG_TYPE + b"\x01" + our_new_strand_nonce + next_hash_chain + message_encrypted,
208+
MSG_TYPE + b"\x01" + our_new_strand_nonce + message_encrypted,
219209
nonce = our_next_strand_nonce
220210
)
221211

@@ -273,16 +263,14 @@ def messages_data_handler(user_data: dict, user_data_lock, user_data_copied: dic
273263

274264
# /32 because KEM shared_secret is 32 bytes, /64 because sha3_512 output is 64 bytes
275265

276-
if len(msgs_plaintext) != ( (ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32)) + (64 * (OTP_PAD_SIZE // 64)) + ML_DSA_87_SIGN_LEN + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1:
266+
if len(msgs_plaintext) != ( (ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32)) + (64 * (OTP_PAD_SIZE // 64)) + ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1:
277267
logger.error("Contact (%s) gave us a otp batch message request with malformed strand plaintext length (%d)", contact_id, len(msgs_plaintext))
278268
return
279269

280-
otp_hashchain_signature = msgs_plaintext[1 + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN: MESSAGE_HASH_CHAIN_LEN + ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1]
281-
otp_hashchain_ciphertext = msgs_plaintext[ML_DSA_87_SIGN_LEN + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1: ML_DSA_87_SIGN_LEN + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1 + ((ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32))]
270+
otp_hashchain_signature = msgs_plaintext[1 + XCHACHA20POLY1305_NONCE_LEN: ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1]
271+
otp_hashchain_ciphertext = msgs_plaintext[ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1: ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1 + ((ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32))]
282272

283-
xchacha_pads = msgs_plaintext[ML_DSA_87_SIGN_LEN + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1 + ((ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32)):]
284-
285-
contact_hash_chain = msgs_plaintext[1 + XCHACHA20POLY1305_NONCE_LEN: MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1]
273+
xchacha_pads = msgs_plaintext[ML_DSA_87_SIGN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1 + ((ML_KEM_1024_CT_LEN + CLASSIC_MCELIECE_8_F_CT_LEN) * (OTP_PAD_SIZE // 32)):]
286274

287275
try:
288276
valid_signature = verify_signature(ML_DSA_87_NAME, otp_hashchain_ciphertext, otp_hashchain_signature, contact_public_key)
@@ -316,10 +304,9 @@ def messages_data_handler(user_data: dict, user_data_lock, user_data_copied: dic
316304

317305

318306
with user_data_lock:
319-
user_data["contacts"][contact_id]["contact_pads"]["pads"] = contact_pads
320-
user_data["contacts"][contact_id]["contact_pads"]["hash_chain"] = contact_hash_chain
307+
user_data["contacts"][contact_id]["contact_pads"] = contact_pads
321308

322-
user_data["contacts"][contact_id]["contact_strand_key"] = contact_strand_key
309+
user_data["contacts"][contact_id]["contact_strand_key"] = contact_strand_key
323310

324311
user_data["contacts"][contact_id]["ephemeral_keys"]["our_keys"][CLASSIC_MCELIECE_8_F_NAME]["rotation_counter"] += 1
325312

@@ -344,25 +331,16 @@ def messages_data_handler(user_data: dict, user_data_lock, user_data_copied: dic
344331
elif msgs_plaintext[0] == 1:
345332
logger.debug("Received a new message from contact (%s).", contact_id)
346333

347-
if len(msgs_plaintext) < OTP_MAX_BUCKET + MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1:
334+
if len(msgs_plaintext) < OTP_MAX_BUCKET + XCHACHA20POLY1305_NONCE_LEN + 1:
348335
logger.error("Contact (%s) gave us a message request with malformed strand plaintext length (%d)", contact_id, len(msgs_plaintext))
349336
return
350337

351338

352-
hash_chain = msgs_plaintext[1 + XCHACHA20POLY1305_NONCE_LEN : MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1]
353-
message_encrypted = msgs_plaintext[MESSAGE_HASH_CHAIN_LEN + XCHACHA20POLY1305_NONCE_LEN + 1:]
339+
message_encrypted = msgs_plaintext[XCHACHA20POLY1305_NONCE_LEN + 1:]
354340

355341

356342
with user_data_lock:
357-
contact_pads = user_data["contacts"][contact_id]["contact_pads"]["pads"]
358-
contact_hash_chain = user_data["contacts"][contact_id]["contact_pads"]["hash_chain"]
359-
360-
361-
next_hash_chain = sha3_512(contact_hash_chain + message_encrypted)
362-
363-
if next_hash_chain != hash_chain:
364-
logger.error("Message hash chain did not match, this could be a possible replay attack, or a failed tampering attempt. Skipping this message...")
365-
return
343+
contact_pads = user_data["contacts"][contact_id]["contact_pads"]
366344

367345

368346
if (not contact_pads) or (len(message_encrypted) > len(contact_pads)):
@@ -376,10 +354,9 @@ def messages_data_handler(user_data: dict, user_data_lock, user_data_copied: dic
376354
contact_pads = contact_pads[len(message_encrypted):]
377355

378356

379-
# and save the new pads and the hash chain
357+
# save the new pads
380358
with user_data_lock:
381-
user_data["contacts"][contact_id]["contact_pads"]["pads"] = contact_pads
382-
user_data["contacts"][contact_id]["contact_pads"]["hash_chain"] = next_hash_chain
359+
user_data["contacts"][contact_id]["contact_pads"] = contact_pads
383360

384361
save_account_data(user_data, user_data_lock)
385362

logic/storage.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,12 @@ def load_account_data(password = None) -> dict:
100100

101101

102102
try:
103-
user_data["contacts"][contact_id]["our_pads"]["pads"] = b64decode(user_data["contacts"][contact_id]["our_pads"]["pads"], validate=True)
104-
user_data["contacts"][contact_id]["our_pads"]["hash_chain"] = b64decode(user_data["contacts"][contact_id]["our_pads"]["hash_chain"], validate=True)
103+
user_data["contacts"][contact_id]["our_pads"] = b64decode(user_data["contacts"][contact_id]["our_pads"], validate=True)
105104
except TypeError:
106105
pass
107106

108107
try:
109-
user_data["contacts"][contact_id]["contact_pads"]["pads"] = b64decode(user_data["contacts"][contact_id]["contact_pads"]["pads"], validate=True)
110-
user_data["contacts"][contact_id]["contact_pads"]["hash_chain"] = b64decode(user_data["contacts"][contact_id]["contact_pads"]["hash_chain"], validate=True)
108+
user_data["contacts"][contact_id]["contact_pads"] = b64decode(user_data["contacts"][contact_id]["contact_pads"], validate=True)
111109
except TypeError:
112110
pass
113111

@@ -195,14 +193,12 @@ def save_account_data(user_data: dict, user_data_lock, password = None) -> None:
195193

196194

197195
try:
198-
user_data["contacts"][contact_id]["our_pads"]["pads"] = b64encode(user_data["contacts"][contact_id]["our_pads"]["pads"]).decode()
199-
user_data["contacts"][contact_id]["our_pads"]["hash_chain"] = b64encode(user_data["contacts"][contact_id]["our_pads"]["hash_chain"]).decode()
196+
user_data["contacts"][contact_id]["our_pads"] = b64encode(user_data["contacts"][contact_id]["our_pads"]).decode()
200197
except TypeError:
201198
pass
202199

203200
try:
204-
user_data["contacts"][contact_id]["contact_pads"]["pads"] = b64encode(user_data["contacts"][contact_id]["contact_pads"]["pads"]).decode()
205-
user_data["contacts"][contact_id]["contact_pads"]["hash_chain"] = b64encode(user_data["contacts"][contact_id]["contact_pads"]["hash_chain"]).decode()
201+
user_data["contacts"][contact_id]["contact_pads"] = b64encode(user_data["contacts"][contact_id]["contact_pads"]).decode()
206202
except TypeError:
207203
pass
208204

0 commit comments

Comments
 (0)