Skip to content

Commit a55e4f7

Browse files
committed
refactor: add argument description to get_card method, improve readability of clean_empty by declaring types, add a multiple signatures test to test_signing.py
1 parent 1a973f0 commit a55e4f7

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

src/a2a/client/base_client.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ async def get_card(
271271
Args:
272272
context: The client call context.
273273
extensions: List of extensions to be activated.
274-
key_provider: A callable that takes key-id (kid) and JSON web key url (jku)
275-
and returns the verification key for signature verification.
274+
signature_verifier: A callable used to verify the agent card's signatures.
276275
277276
Returns:
278277
The `AgentCard` for the agent.

src/a2a/utils/signing.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@
2020

2121

2222
def clean_empty(d: Any) -> Any:
23-
"""Recursively remove empty lists, dicts, strings, and None values from a dictionary."""
23+
"""Recursively remove empty strings, lists, dicts, and None values from a dictionary."""
2424
if isinstance(d, dict):
25-
cleaned = {k: clean_empty(v) for k, v in d.items()}
25+
cleaned_dict: dict[Any, Any] = {k: clean_empty(v) for k, v in d.items()}
2626
return {
2727
k: v
28-
for k, v in cleaned.items()
28+
for k, v in cleaned_dict.items()
2929
if v is not None and (isinstance(v, (bool, int, float)) or v)
3030
}
3131
if isinstance(d, list):
32-
cleaned = [clean_empty(v) for v in d]
32+
cleaned_list: list[Any] = [clean_empty(v) for v in d]
3333
return [
3434
v
35-
for v in cleaned
35+
for v in cleaned_list
3636
if v is not None and (isinstance(v, (bool, int, float)) or v)
3737
]
38-
return d if d not in [None, '', [], {}] else None
38+
return d if d not in ['', [], {}, None] else None
3939

4040

4141
def canonicalize_agent_card(agent_card: AgentCard) -> str:
@@ -151,11 +151,13 @@ def signature_verifier(
151151
key=verification_key,
152152
algorithms=None,
153153
)
154-
return # Found a valid signature
155-
154+
# Found a valid signature, exit the loop and function
155+
break
156156
except JOSEError as e:
157157
last_error = e
158158
continue
159-
raise JOSEError('No valid signature found') from last_error
159+
else:
160+
# This block runs only if the loop completes without a break
161+
raise JOSEError('No valid signature found') from last_error
160162

161163
return signature_verifier

tests/utils/test_signing.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
AgentCard,
88
AgentCapabilities,
99
AgentSkill,
10+
AgentCardSignature,
1011
)
1112
from a2a.utils.signing import (
1213
canonicalize_agent_card,
@@ -16,6 +17,7 @@
1617
from typing import Any
1718
from jose.backends.base import Key
1819
from jose.exceptions import JOSEError
20+
from jose.utils import base64url_encode
1921

2022
import pytest
2123
from cryptography.hazmat.primitives import asymmetric
@@ -86,6 +88,46 @@ def test_signer_and_verifier_symmetric(sample_agent_card: AgentCard):
8688
verifier_wrong_key(signed_card)
8789

8890

91+
def test_signer_and_verifier_symmetric_multiple_signatures(
92+
sample_agent_card: AgentCard,
93+
):
94+
"""Test the agent card signing and verification process with symmetric key encryption.
95+
This test adds a signatures to the AgentCard before signing."""
96+
encoded_header = base64url_encode(
97+
b'{"alg": "HS256", "kid": "old_key"}'
98+
).decode('utf-8')
99+
sample_agent_card.signatures = [
100+
AgentCardSignature(protected=encoded_header, signature='old_signature')
101+
]
102+
key = 'key12345' # Using a simple symmetric key for HS256
103+
wrong_key = 'wrongkey'
104+
105+
agent_card_signer = create_agent_card_signer(
106+
signing_key=key, alg='HS384', kid='key1'
107+
)
108+
signed_card = agent_card_signer(sample_agent_card)
109+
110+
assert signed_card.signatures is not None
111+
assert len(signed_card.signatures) == 2
112+
signature = signed_card.signatures[1]
113+
assert signature.protected is not None
114+
assert signature.signature is not None
115+
116+
# Verify the signature
117+
verifier = create_signature_verifier(create_key_provider(key))
118+
try:
119+
verifier(signed_card)
120+
except JOSEError:
121+
pytest.fail('Signature verification failed with correct key')
122+
123+
# Verify with wrong key
124+
verifier_wrong_key = create_signature_verifier(
125+
create_key_provider(wrong_key)
126+
)
127+
with pytest.raises(JOSEError):
128+
verifier_wrong_key(signed_card)
129+
130+
89131
def test_signer_and_verifier_asymmetric(sample_agent_card: AgentCard):
90132
"""Test the agent card signing and verification process with an asymmetric key encryption."""
91133
# Generate a dummy EC private key for ES256

0 commit comments

Comments
 (0)