File tree Expand file tree Collapse file tree 2 files changed +46
-1
lines changed
Expand file tree Collapse file tree 2 files changed +46
-1
lines changed Original file line number Diff line number Diff line change 99import urllib .parse
1010from typing import Literal , TypedDict , cast , overload
1111import datetime
12+ from datetime import timezone
1213
1314# Define algorithms
1415SHA1 : Literal ["SHA-1" ] = "SHA-1"
@@ -426,7 +427,16 @@ def create_challenge(
426427 salt_params = dict (urllib .parse .parse_qsl (salt_query ))
427428
428429 if options .expires :
429- salt_params ["expires" ] = str (int (time .mktime (options .expires .timetuple ())))
430+ expires = options .expires
431+
432+ if expires .tzinfo is None :
433+ # Backward compatibility: assume naive datetimes are local time
434+ timestamp = int (time .mktime (expires .timetuple ()))
435+ else :
436+ # Aware datetimes: use true UTC timestamp
437+ timestamp = int (expires .timestamp ())
438+
439+ salt_params ["expires" ] = str (timestamp )
430440
431441 if options .params :
432442 salt_params .update (options .params )
@@ -723,3 +733,14 @@ def solve_challenge(
723733 return Solution (n , took )
724734
725735 return None
736+
737+ def _normalize_expires (expires : datetime .datetime | None ) -> datetime .datetime | None :
738+ if expires is None :
739+ return None
740+
741+ # Case 1: naive datetime → assume UTC (backward compatibility)
742+ if expires .tzinfo is None :
743+ return expires .replace (tzinfo = timezone .utc )
744+
745+ # Case 2: aware datetime (any timezone) → convert to UTC
746+ return expires .astimezone (timezone .utc )
Original file line number Diff line number Diff line change @@ -129,6 +129,30 @@ def test_verify_solution_max_number(self):
129129 result , _ = verify_solution (payload_encoded , self .hmac_key , check_expires = False )
130130 self .assertTrue (result )
131131
132+ def test_verify_solution_expires_utc (self ):
133+ options = ChallengeOptions (
134+ algorithm = "SHA-256" ,
135+ max_number = 1000 ,
136+ salt_length = 16 ,
137+ hmac_key = self .hmac_key ,
138+ salt = "somesalt" ,
139+ number = 123 ,
140+ expires = datetime .datetime .now (datetime .timezone .utc ),
141+ )
142+ challenge = create_challenge (options )
143+ payload = Payload (
144+ algorithm = "SHA-256" ,
145+ challenge = challenge .challenge ,
146+ number = 123 ,
147+ salt = challenge .salt ,
148+ signature = challenge .signature ,
149+ )
150+ payload_encoded = base64 .b64encode (
151+ json .dumps (payload .__dict__ ).encode ()
152+ ).decode ()
153+ result , _ = verify_solution (payload_encoded , self .hmac_key , check_expires = True )
154+ self .assertFalse (result )
155+
132156 def test_verify_solution_not_expired (self ):
133157 options = ChallengeOptions (
134158 algorithm = "SHA-256" ,
You can’t perform that action at this time.
0 commit comments