test_sign.py
1 """Tests for signing and action envelope functions.""" 2 3 import json 4 5 import pytest 6 from auths import sign_bytes, sign_action, verify_action_envelope 7 8 TEST_SEED_HEX = "a" * 64 9 10 11 class TestSignBytes: 12 13 def test_sign_returns_hex_signature(self): 14 sig = sign_bytes(TEST_SEED_HEX, b"hello world") 15 assert len(sig) == 128 16 bytes.fromhex(sig) 17 18 def test_sign_deterministic(self): 19 sig1 = sign_bytes(TEST_SEED_HEX, b"test message") 20 sig2 = sign_bytes(TEST_SEED_HEX, b"test message") 21 assert sig1 == sig2 22 23 def test_sign_different_messages_differ(self): 24 sig1 = sign_bytes(TEST_SEED_HEX, b"message one") 25 sig2 = sign_bytes(TEST_SEED_HEX, b"message two") 26 assert sig1 != sig2 27 28 def test_sign_invalid_key_hex(self): 29 with pytest.raises(ValueError, match="(?i)hex"): 30 sign_bytes("not-hex", b"hello") 31 32 def test_sign_wrong_key_length(self): 33 with pytest.raises(ValueError, match="(?i)length"): 34 sign_bytes("abcd", b"hello") 35 36 37 class TestSignAction: 38 39 def test_sign_action_returns_valid_envelope(self): 40 envelope_json = sign_action( 41 TEST_SEED_HEX, "tool_call", '{"tool": "read_file"}', "did:keri:ETest123", 42 ) 43 envelope = json.loads(envelope_json) 44 assert envelope["version"] == "1.0" 45 assert envelope["type"] == "tool_call" 46 assert envelope["identity"] == "did:keri:ETest123" 47 assert envelope["payload"] == {"tool": "read_file"} 48 assert "timestamp" in envelope 49 assert "signature" in envelope 50 assert len(envelope["signature"]) == 128 51 52 def test_sign_action_invalid_key(self): 53 with pytest.raises(ValueError, match="(?i)hex"): 54 sign_action("not-hex", "test", "{}", "did:keri:E123") 55 56 def test_sign_action_invalid_json(self): 57 with pytest.raises(ValueError, match="(?i)json"): 58 sign_action(TEST_SEED_HEX, "test", "not json", "did:keri:E123") 59 60 61 class TestSignAndVerifyRoundtrip: 62 63 def _get_public_key_hex(self): 64 try: 65 from cryptography.hazmat.primitives.asymmetric.ed25519 import ( 66 Ed25519PrivateKey, 67 ) 68 seed_bytes = bytes.fromhex(TEST_SEED_HEX) 69 private_key = Ed25519PrivateKey.from_private_bytes(seed_bytes) 70 pub_bytes = private_key.public_key().public_bytes_raw() 71 return pub_bytes.hex() 72 except ImportError: 73 pytest.skip("cryptography package not installed") 74 75 def test_sign_and_verify_roundtrip(self): 76 pub_hex = self._get_public_key_hex() 77 envelope_json = sign_action( 78 TEST_SEED_HEX, "tool_call", 79 '{"tool": "read_file", "path": "/etc/config.json"}', 80 "did:keri:ETest123", 81 ) 82 result = verify_action_envelope(envelope_json, pub_hex) 83 assert result.valid, f"Roundtrip verification failed: {result.error}" 84 85 def test_verify_rejects_tampered_payload(self): 86 pub_hex = self._get_public_key_hex() 87 envelope_json = sign_action( 88 TEST_SEED_HEX, "tool_call", '{"tool": "safe_action"}', "did:keri:ETest123", 89 ) 90 envelope = json.loads(envelope_json) 91 envelope["payload"]["tool"] = "malicious_action" 92 tampered_json = json.dumps(envelope) 93 result = verify_action_envelope(tampered_json, pub_hex) 94 assert not result.valid 95 96 def test_verify_rejects_wrong_public_key(self): 97 wrong_pk_hex = "b" * 64 98 envelope_json = sign_action( 99 TEST_SEED_HEX, "tool_call", '{"tool": "read_file"}', "did:keri:ETest123", 100 ) 101 result = verify_action_envelope(envelope_json, wrong_pk_hex) 102 assert not result.valid 103 104 105 class TestVerifyActionEnvelope: 106 107 def test_verify_invalid_envelope_json(self): 108 with pytest.raises(ValueError, match="(?i)json"): 109 verify_action_envelope("not json", "a" * 64) 110 111 def test_verify_invalid_public_key_hex(self): 112 with pytest.raises(ValueError, match="(?i)hex"): 113 verify_action_envelope("{}", "not-hex") 114 115 def test_verify_wrong_public_key_length(self): 116 with pytest.raises(ValueError, match="(?i)length"): 117 verify_action_envelope("{}", "abcd") 118 119 def test_verify_missing_version_field(self): 120 envelope = json.dumps({"type": "test", "signature": "aa" * 64}) 121 with pytest.raises(ValueError, match="version"): 122 verify_action_envelope(envelope, "a" * 64) 123 124 def test_verify_unsupported_version(self): 125 envelope = json.dumps({ 126 "version": "99.0", "type": "test", "identity": "did:keri:E123", 127 "payload": {}, "timestamp": "2025-01-01T00:00:00Z", "signature": "aa" * 64, 128 }) 129 result = verify_action_envelope(envelope, "a" * 64) 130 assert not result.valid 131 assert "version" in result.error.lower()