Skip to content

Commit

Permalink
Merge pull request #11 from stealthrocket/max-clock-skew
Browse files Browse the repository at this point in the history
add max_clock_skew to HTTPMessageVerifier
  • Loading branch information
kislyuk authored Feb 20, 2024
2 parents 90c1a8e + 64cbdcb commit c6bbddc
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 3 deletions.
9 changes: 6 additions & 3 deletions http_message_signatures/signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def sign(


class HTTPMessageVerifier(HTTPSignatureHandler):
max_clock_skew: datetime.timedelta = datetime.timedelta(seconds=5)
require_created: bool = True

def _parse_dict_header(self, header_name, headers):
Expand All @@ -133,16 +134,18 @@ def _parse_integer_timestamp(self, ts, field_name):

def validate_created_and_expires(self, sig_input, max_age=None):
now = datetime.datetime.now()
min_time = now - self.max_clock_skew
max_time = now + self.max_clock_skew
if "created" in sig_input.params:
if self._parse_integer_timestamp(sig_input.params["created"], field_name="created") > now:
if self._parse_integer_timestamp(sig_input.params["created"], field_name="created") > max_time:
raise InvalidSignature('Signature "created" parameter is set to a time in the future')
elif self.require_created:
raise InvalidSignature('Signature is missing a required "created" parameter')
if "expires" in sig_input.params:
if self._parse_integer_timestamp(sig_input.params["expires"], field_name="expires") < now:
if self._parse_integer_timestamp(sig_input.params["expires"], field_name="expires") < min_time:
raise InvalidSignature('Signature "expires" parameter is set to a time in the past')
if max_age is not None:
if self._parse_integer_timestamp(sig_input.params["created"], field_name="created") + max_age < now:
if self._parse_integer_timestamp(sig_input.params["created"], field_name="created") + max_age < min_time:
raise InvalidSignature(f"Signature age exceeds maximum allowable age {max_age}")

def verify(self, message, *, max_age: datetime.timedelta = datetime.timedelta(days=1)) -> List[VerifyResult]:
Expand Down
10 changes: 10 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,16 @@ def test_created_expires(self):
with self.assertRaisesRegex(InvalidSignature, 'Signature "expires" parameter is set to a time in the past'):
verifier.verify(self.test_request)

def test_tolerate_clock_skew(self):
signer = HTTPMessageSigner(signature_algorithm=HMAC_SHA256, key_resolver=self.key_resolver)
signer.sign(self.test_request, key_id="test-shared-secret", created=datetime.fromtimestamp(1))
verifier = HTTPMessageVerifier(signature_algorithm=HMAC_SHA256, key_resolver=self.key_resolver)
signer.sign(self.test_request, key_id="test-shared-secret", created=datetime.now() + timedelta(seconds=9))
verifier.max_clock_skew = timedelta(seconds=10)
verifier.verify(self.test_request)
verifier.max_clock_skew = timedelta(seconds=0)
with self.assertRaisesRegex(InvalidSignature, 'Signature "created" parameter is set to a time in the future'):
verifier.verify(self.test_request)

if __name__ == "__main__":
unittest.main()

0 comments on commit c6bbddc

Please sign in to comment.