Skip to content

Commit dfe6dec

Browse files
committed
refactor: add more function documentation
1 parent e832d98 commit dfe6dec

File tree

3 files changed

+107
-24
lines changed

3 files changed

+107
-24
lines changed

logic/authentication.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
logic/authentication.py
3+
----------
4+
Implements client-side account authentication with the Coldwire server.
5+
Uses ML-DSA-87 signatures to sign a challenge to verify account ownership.
6+
7+
"""
18
from base64 import b64encode, b64decode
29
from core.requests import http_request
310
from core.crypto import create_signature
@@ -7,6 +14,21 @@
714
)
815

916
def authenticate_account(user_data: dict) -> dict:
17+
"""
18+
Authenticate an account with the Coldwire server.
19+
20+
Args:
21+
user_data (dict): Shared user account state.
22+
23+
Returns:
24+
dict with the user_data containing:
25+
- "user_id" (str): Server-assigned user ID.
26+
- "token" (str): Session token issued by the server.
27+
28+
Raises:
29+
ValueError: If the server cannot be reached, gives malformed responses, or if authentication fails.
30+
31+
"""
1032
url = user_data["server_url"]
1133

1234
private_key = user_data["lt_auth_sign_keys"]["private_key"]

logic/message.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""
2-
logic/message.py
3-
-----------
4-
Message sending, receiving, and one-time-pad key exchange logic.
5-
Handles:
6-
- Generation and transmission of hybrid ciphertext OTP batches
7-
- Ephemeral key rotation enforcement for PFS
8-
- Message encryption/decryption with hash chain integrity checks
9-
- Incoming message processing and replay/tampering protection
2+
logic/message.py
3+
-----------
4+
Message sending, receiving, and one-time-pad key exchange logic.
5+
6+
Handles:
7+
- Generation and transmission of hybrid ciphertext OTP batches
8+
- Ephemeral key rotation enforcement for PFS
9+
- Message encryption/decryption with hash chain integrity checks
10+
- Incoming message processing and replay/tampering protection
1011
"""
1112

1213
from core.requests import http_request

logic/pfs.py

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
"""
2+
logic/pfs.py
3+
-----------------
4+
Handles Perfect Forward Secrecy (PFS) ephemeral keys exchange and rotation for contacts.
5+
6+
Handles:
7+
- Generates and rotates ephemeral (one-time use) ML-KEM-1024 keys and (medium-term) Classic McEliece keys.
8+
- Uses per-contact hash chains to prevent replay attacks, and verifies authenticity using ML-DSA-87.
9+
- Sends and receives signed ephemeral keys using long-term signing keys.
10+
- Updates local account storage with new key material after successful exchange.
11+
"""
12+
113
from core.requests import http_request
214
from logic.storage import save_account_data
315
from core.crypto import (
@@ -20,19 +32,38 @@
2032
import copy
2133
import json
2234
import logging
35+
import threading
36+
import queue
2337

2438
logger = logging.getLogger(__name__)
2539

2640

27-
def send_new_ephemeral_keys(user_data, user_data_lock, contact_id, ui_queue) -> None:
41+
def send_new_ephemeral_keys(user_data: dict, user_data_lock: threading.Lock, contact_id: str, ui_queue: queue.Queue) -> None:
42+
"""
43+
Generate and send fresh ephemeral keys to a contact.
44+
45+
- Maintains a per-contact hash chain for signing key material.
46+
- Generates new Kyber1024 keys every call.
47+
- Optionally rotates McEliece keys if rotation threshold is reached.
48+
- Signs all key material with the long-term signing key.
49+
- Sends to the server using an authenticated HTTP request.
50+
- If successful, stores new keys in `user_data["tmp"]` for later update.
51+
52+
Args:
53+
user_data (dict): Shared user account state.
54+
user_data_lock (threading.Lock): Lock protecting shared state.
55+
contact_id (str): Target contact's ID.
56+
ui_queue (queue.Queue): UI queue for showing error messages.
57+
"""
58+
2859
with user_data_lock:
2960
user_data_copied = copy.deepcopy(user_data)
30-
31-
rotation_counter = user_data["contacts"][contact_id]["ephemeral_keys"]["our_keys"][CLASSIC_MCELIECE_8_F_NAME]["rotation_counter"]
32-
rotate_at = user_data["contacts"][contact_id]["ephemeral_keys"]["our_keys"][CLASSIC_MCELIECE_8_F_NAME]["rotate_at"]
61+
62+
server_url = user_data_copied["server_url"]
63+
auth_token = user_data_copied["token"]
3364

34-
server_url = user_data["server_url"]
35-
auth_token = user_data["token"]
65+
rotation_counter = user_data_copied["contacts"][contact_id]["ephemeral_keys"]["our_keys"][CLASSIC_MCELIECE_8_F_NAME]["rotation_counter"]
66+
rotate_at = user_data_copied["contacts"][contact_id]["ephemeral_keys"]["our_keys"][CLASSIC_MCELIECE_8_F_NAME]["rotate_at"]
3667

3768
lt_sign_private_key = user_data_copied["contacts"][contact_id]["lt_sign_keys"]["our_keys"]["private_key"]
3869

@@ -48,7 +79,7 @@ def send_new_ephemeral_keys(user_data, user_data_lock, contact_id, ui_queue) ->
4879
# We continue the hash chain
4980
our_hash_chain = sha3_512(our_hash_chain)
5081

51-
# Generate new Kyber1024 keys for us
82+
# Generate new ML-KEM-1024 keys for us
5283
kyber_private_key, kyber_public_key = generate_kem_keys(ML_KEM_1024_NAME)
5384
publickeys_hashchain = our_hash_chain + kyber_public_key
5485

@@ -100,7 +131,18 @@ def send_new_ephemeral_keys(user_data, user_data_lock, contact_id, ui_queue) ->
100131

101132

102133

103-
def update_ephemeral_keys(user_data, user_data_lock) -> None:
134+
def update_ephemeral_keys(user_data: dict, user_data_lock: threading.Lock) -> None:
135+
"""
136+
Commit newly generated ephemeral keys to permanent storage.
137+
138+
- Moves keys from `user_data["tmp"]` into `user_data["contacts"]`.
139+
- Clears temporary key buffers after update.
140+
- Saves account state to disk.
141+
142+
Args:
143+
user_data (dict): Shared user account state.
144+
user_data_lock (threading.Lock): Lock protecting shared state.
145+
"""
104146
with user_data_lock:
105147
new_ml_kem_keys = user_data["tmp"]["new_ml_kem_keys"]
106148
new_code_kem_keys = user_data["tmp"]["new_code_kem_keys"]
@@ -126,24 +168,42 @@ def update_ephemeral_keys(user_data, user_data_lock) -> None:
126168

127169

128170

129-
def pfs_data_handler(user_data, user_data_lock, user_data_copied, ui_queue, message) -> None:
171+
def pfs_data_handler(user_data: dict, user_data_lock: threading.Lock, user_data_copied: dict, ui_queue: queue.Queue, message: dict) -> None:
172+
"""
173+
Handle incoming PFS (Perfect Forward Secrecy) key messages from contacts.
174+
175+
- Validates the contact exists and their signing key is verified.
176+
- Verifies the signature on the ephemeral keys + hash chain.
177+
- Updates stored contact ephemeral keys and hash chains.
178+
- If we don't have keys yet for this contact, triggers sending ours.
179+
- Saves updated account state to disk.
180+
181+
Args:
182+
user_data (dict): Shared user account state.
183+
user_data_lock (threading.Lock): Lock protecting shared state.
184+
user_data_copied (dict): A read-only copy of user_data for consistency.
185+
ui_queue (queue.Queue): UI queue for notifications/errors.
186+
message (dict): Incoming PFS message from the server.
187+
188+
Returns:
189+
None
190+
"""
130191
contact_id = message["sender"]
131192

132193
if contact_id not in user_data_copied["contacts"]:
133-
logger.error("Contact is missing, maybe we (or they) are not synced? Not sure, but we will ignore this PFS request for now")
134-
logger.debug("Our saved contacts: %s", json.dumps(user_data_copied["contacts"], indent=2))
194+
logger.error("Contact is not saved., maybe we (or they) are not synced? Ignoring this PFS message.")
195+
logger.debug("Our saved contacts: %s", str(user_data_copied["contacts"]))
135196
return
136197

137-
# Contact's main long-term public signing key
198+
# Contact's per-contact signing public-key
138199
contact_lt_public_key = user_data_copied["contacts"][contact_id]["lt_sign_keys"]["contact_public_key"]
139200

140-
141201
if not contact_lt_public_key:
142202
logger.error("Contact long-term signing key is missing... 0 clue how we reached here, but we aint continuing..")
143203
return
144204

145205
if not user_data_copied["contacts"][contact_id]["lt_sign_key_smp"]["verified"]:
146-
logger.error("Contact long-term signing key is not verified! it is possible that this is a MiTM attack, we ignoring this PFS for now.")
206+
logger.error("Contact long-term signing key is not verified! We will ignore this PFS message.")
147207
return
148208

149209
contact_hashchain_signature = b64decode(message["hashchain_signature"], validate=True)
@@ -170,7 +230,7 @@ def pfs_data_handler(user_data, user_data_lock, user_data_copied, ui_queue, mess
170230
contact_last_hash_chain = sha3_512(contact_last_hash_chain)
171231

172232
if contact_last_hash_chain != contact_hash_chain:
173-
logger.error("Contact hash chain does not match our computed hash chain, we are skipping this PFS message...")
233+
logger.error("Contact keys hash chain does not match our computed hash chain! Skipping this PFS message...")
174234
return
175235

176236
contact_kyber_public_key = contact_publickeys_hashchain[KEYS_HASH_CHAIN_LEN: ALGOS_BUFFER_LIMITS[ML_KEM_1024_NAME]["PK_LEN"] + KEYS_HASH_CHAIN_LEN]
@@ -195,8 +255,8 @@ def pfs_data_handler(user_data, user_data_lock, user_data_copied, ui_queue, mess
195255
new_code_kem_keys = user_data["tmp"]["new_code_kem_keys"]
196256

197257
if (our_kyber_private_key is None or our_mceliece_private_key is None) and ((contact_id not in new_ml_kem_keys) and (contact_id not in new_code_kem_keys)):
198-
send_new_ephemeral_keys(user_data, user_data_lock, contact_id, ui_queue)
199258
logger.info("We are sending the contact (%s) our ephemeral keys because we didnt do it before.", contact_id)
259+
send_new_ephemeral_keys(user_data, user_data_lock, contact_id, ui_queue)
200260

201261
save_account_data(user_data, user_data_lock)
202262

0 commit comments

Comments
 (0)