/ packages / auths-python / tests / test_sign.py
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()