|
| 1 | +# Copyright (c) Microsoft Corporation. |
| 2 | +# Licensed under the MIT License. |
| 3 | + |
| 4 | +import pytest |
| 5 | +from pydantic import BaseModel |
| 6 | +from datetime import datetime, timezone |
| 7 | +from .. import crypto |
| 8 | + |
| 9 | +class MockModel(BaseModel): |
| 10 | + name: str |
| 11 | + value: int |
| 12 | + timestamp: datetime |
| 13 | + signature: str = "" |
| 14 | + |
| 15 | +def test_canonical_payload_stability(): |
| 16 | + """Ensure dict and pydantic models produce same canonical output.""" |
| 17 | + ts = datetime(2024, 1, 1, tzinfo=timezone.utc) |
| 18 | + data_dict = {"name": "test", "value": 42, "timestamp": ts, "signature": "ignore-me"} |
| 19 | + data_model = MockModel(name="test", value=42, timestamp=ts, signature="ignore-me") |
| 20 | + |
| 21 | + payload1 = crypto.canonical_payload(data_dict) |
| 22 | + payload2 = crypto.canonical_payload(data_model) |
| 23 | + |
| 24 | + assert payload1 == payload2 |
| 25 | + # Check that signature field is excluded |
| 26 | + assert b"ignore-me" not in payload1 |
| 27 | + # Check that keys are sorted (name before value) |
| 28 | + assert payload1.find(b"name") < payload1.find(b"value") |
| 29 | + |
| 30 | +def test_sign_and_verify_roundtrip(): |
| 31 | + """Test full sign/verify cycle.""" |
| 32 | + priv, pub_str = crypto.generate_keypair() |
| 33 | + data = {"agent_did": "did:nexus:test", "action": "register"} |
| 34 | + |
| 35 | + sig_hex = crypto.sign_data(priv, data) |
| 36 | + assert len(sig_hex) == 128 # 64 bytes hex encoded |
| 37 | + |
| 38 | + # Should not raise |
| 39 | + crypto.verify_signature(pub_str, sig_hex, data) |
| 40 | + |
| 41 | +def test_verify_fails_on_tamper(): |
| 42 | + """Test that tampered data fails verification.""" |
| 43 | + priv, pub_str = crypto.generate_keypair() |
| 44 | + data = {"agent_did": "did:nexus:test", "action": "register"} |
| 45 | + sig_hex = crypto.sign_data(priv, data) |
| 46 | + |
| 47 | + tampered_data = {"agent_did": "did:nexus:EVIL", "action": "register"} |
| 48 | + |
| 49 | + with pytest.raises(crypto.SignatureVerificationError): |
| 50 | + crypto.verify_signature(pub_str, sig_hex, tampered_data) |
| 51 | + |
| 52 | +def test_verify_fails_on_wrong_key(): |
| 53 | + """Test that verification fails with a different key.""" |
| 54 | + priv1, pub_str1 = crypto.generate_keypair() |
| 55 | + priv2, pub_str2 = crypto.generate_keypair() |
| 56 | + |
| 57 | + data = {"agent_did": "did:nexus:test"} |
| 58 | + sig_hex = crypto.sign_data(priv1, data) |
| 59 | + |
| 60 | + with pytest.raises(crypto.SignatureVerificationError): |
| 61 | + crypto.verify_signature(pub_str2, sig_hex, data) |
| 62 | + |
| 63 | +def test_decode_error(): |
| 64 | + """Test behavior with malformed hex.""" |
| 65 | + _, pub_str = crypto.generate_keypair() |
| 66 | + with pytest.raises(crypto.SignatureDecodeError): |
| 67 | + crypto.verify_signature(pub_str, "not-a-hex-string", {"data": 1}) |
0 commit comments