22
33from __future__ import annotations
44
5+ import importlib .util
6+ from datetime import datetime , timezone
7+
58import pytest
69
7- try :
8- import rfc8785 # noqa: F401
9- from cryptography . hazmat . primitives . asymmetric import ec , rsa
10-
11- from vaara . attestation . sep2787 import (
12- ArgsRef ,
13- Attestation ,
14- AttestationError ,
15- IssuerAsserted ,
16- PlannerDeclared ,
17- ToolCallBinding ,
18- canonical_json ,
19- emit_attestation ,
20- make_args_digest ,
21- make_args_projection ,
22- verify_attestation ,
23- )
24- except ImportError :
25- pytest . skip (
26- "attestation extra not installed (pip install 'vaara[attestation]')" ,
27- allow_module_level = True ,
28- )
10+ for _mod in ( "rfc8785" , "cryptography" ) :
11+ if importlib . util . find_spec ( _mod ) is None :
12+ pytest . skip (
13+ "attestation extra not installed (pip install 'vaara[attestation]')" ,
14+ allow_module_level = True ,
15+ )
16+
17+ from cryptography . hazmat . primitives . asymmetric import ec , rsa # noqa: E402
18+
19+ from vaara . attestation . sep2787 import ( # noqa: E402
20+ ArgsRef ,
21+ Attestation ,
22+ AttestationError ,
23+ IssuerAsserted ,
24+ PlannerDeclared ,
25+ ToolCallBinding ,
26+ canonical_json ,
27+ emit_attestation ,
28+ make_args_digest ,
29+ make_args_projection ,
30+ verify_attestation ,
31+ )
2932
3033
3134HS_SECRET = b"\x42 " * 32
@@ -46,7 +49,7 @@ def _planner(args=None) -> PlannerDeclared:
4649 )
4750
4851
49- def _emit_hs256 (** overrides ) -> Attestation :
52+ def _emit_attestation (** overrides ) -> Attestation :
5053 kwargs = dict (
5154 planner_declared = _planner (),
5255 iss = "issuer://test" ,
@@ -60,7 +63,7 @@ def _emit_hs256(**overrides) -> Attestation:
6063
6164
6265def test_hs256_emit_and_verify_round_trip ():
63- env = _emit_hs256 ()
66+ env = _emit_attestation ()
6467 assert env .alg == "HS256"
6568 assert env .version == 1
6669 assert env .signature
@@ -69,19 +72,19 @@ def test_hs256_emit_and_verify_round_trip():
6972
7073def test_es256_emit_and_verify_round_trip ():
7174 priv = ec .generate_private_key (ec .SECP256R1 ())
72- env = _emit_hs256 (alg = "ES256" , signing_material = priv )
75+ env = _emit_attestation (alg = "ES256" , signing_material = priv )
7376 assert len (env .signature ) == 128
7477 assert verify_attestation (env , verifying_material = priv .public_key ()) is True
7578
7679
7780def test_rs256_emit_and_verify_round_trip ():
7881 priv = rsa .generate_private_key (public_exponent = 65537 , key_size = 2048 )
79- env = _emit_hs256 (alg = "RS256" , signing_material = priv )
82+ env = _emit_attestation (alg = "RS256" , signing_material = priv )
8083 assert verify_attestation (env , verifying_material = priv .public_key ()) is True
8184
8285
8386def test_tampered_planner_block_fails_verification ():
84- env = _emit_hs256 ()
87+ env = _emit_attestation ()
8588 tampered_planner = PlannerDeclared (
8689 intent = "archive obsolete report per Q4 retention policy" ,
8790 tool_calls = env .planner_declared .tool_calls ,
@@ -99,7 +102,7 @@ def test_tampered_planner_block_fails_verification():
99102
100103
101104def test_tampered_issuer_block_fails_verification ():
102- env = _emit_hs256 ()
105+ env = _emit_attestation ()
103106 tampered_issuer = IssuerAsserted (
104107 iss = env .issuer_asserted .iss ,
105108 sub = "agent:HIJACKED" ,
@@ -125,7 +128,7 @@ def test_args_digest_round_trip_three_shapes():
125128 r = ArgsRef (ref = "ipfs://Qm..." , digest = "sha256:" + "0" * 64 )
126129 p = make_args_projection ({"redacted_user_id" : "u-001" })
127130 for args in (d , r , p ):
128- env = _emit_hs256 (planner_declared = _planner (args = args ))
131+ env = _emit_attestation (planner_declared = _planner (args = args ))
129132 assert verify_attestation (env , verifying_material = HS_SECRET )
130133 assert env .payload_derived [0 ].kind == args .kind
131134
@@ -154,34 +157,50 @@ def test_make_args_digest_is_deterministic():
154157
155158
156159def test_ttl_expired_returns_false ():
157- env = _emit_hs256 (exp_seconds = 60 )
160+ env = _emit_attestation (exp_seconds = 60 )
158161 far_future = 9_999_999_999.0
159162 assert verify_attestation (
160163 env , verifying_material = HS_SECRET , now = far_future ,
161164 ) is False
162165
163166
167+ def test_ttl_clock_skew_tolerance_window ():
168+ iat = "2026-05-26T12:00:00Z"
169+ iat_epoch = datetime (
170+ 2026 , 5 , 26 , 12 , 0 , 0 , tzinfo = timezone .utc ,
171+ ).timestamp ()
172+ env = _emit_attestation (exp_seconds = 60 , iat = iat )
173+ # default clock_skew_seconds=30: iat + 60 + 15 still inside window
174+ assert verify_attestation (
175+ env , verifying_material = HS_SECRET , now = iat_epoch + 60 + 15 ,
176+ ) is True
177+ # iat + 60 + 31 is past the default skew window
178+ assert verify_attestation (
179+ env , verifying_material = HS_SECRET , now = iat_epoch + 60 + 31 ,
180+ ) is False
181+
182+
164183def test_emit_rejects_empty_intent ():
165184 bad = PlannerDeclared (
166185 intent = " " ,
167186 tool_calls = _planner ().tool_calls ,
168187 )
169188 with pytest .raises (AttestationError , match = "intent" ):
170- _emit_hs256 (planner_declared = bad )
189+ _emit_attestation (planner_declared = bad )
171190
172191
173192def test_emit_rejects_unsupported_alg ():
174193 with pytest .raises (AttestationError , match = "unsupported alg" ):
175- _emit_hs256 (alg = "HS512" )
194+ _emit_attestation (alg = "HS512" )
176195
177196
178197def test_hs256_rejects_non_bytes_secret ():
179198 with pytest .raises (AttestationError , match = "bytes shared_secret" ):
180- _emit_hs256 (signing_material = "not-bytes" )
199+ _emit_attestation (signing_material = "not-bytes" )
181200
182201
183202def test_to_dict_round_trip_shape ():
184- env = _emit_hs256 ()
203+ env = _emit_attestation ()
185204 d = env .to_dict ()
186205 assert set (d ) == {
187206 "version" , "alg" , "planner_declared" ,
@@ -192,6 +211,6 @@ def test_to_dict_round_trip_shape():
192211
193212
194213def test_cross_alg_verification_fails ():
195- env = _emit_hs256 ()
214+ env = _emit_attestation ()
196215 priv = ec .generate_private_key (ec .SECP256R1 ())
197216 assert verify_attestation (env , verifying_material = priv .public_key ()) is False
0 commit comments