killinchu / tests /test_receipt_export_contract.py
betterwithage's picture
fix: make receipt export bounded and evidence-honest (#7)
01a4407
Raw
History Blame Contribute Delete
4.67 kB
import base64
import hashlib
import hmac
import json
import unittest
from killinchu_receipt_export import SCHEMA, build_receipt_export
class _TestDSSE:
"""Deterministic verifier used to test the route contract without secrets."""
secret = b"test-only-key"
@staticmethod
def canonical_json(obj):
return json.dumps(obj, sort_keys=True, separators=(",", ":")).encode("utf-8")
@staticmethod
def pae(payload_type, body):
t = payload_type.encode("utf-8")
return b"DSSEv1 " + str(len(t)).encode() + b" " + t + b" " + str(len(body)).encode() + b" " + body
@classmethod
def signature(cls, payload_type, receipt):
body = cls.canonical_json(receipt)
return base64.b64encode(hmac.new(cls.secret, cls.pae(payload_type, body), hashlib.sha256).digest()).decode()
@classmethod
def verify_envelope(cls, env):
body = base64.b64decode(env["payload"])
expected = hmac.new(cls.secret, cls.pae(env["payloadType"], body), hashlib.sha256).digest()
actual = base64.b64decode(env["signatures"][0]["sig"])
verified = hmac.compare_digest(actual, expected)
return {
"verified": verified,
"reason": "test signature mismatch" if not verified else "verified",
"pae_sha256": hashlib.sha256(cls.pae(env["payloadType"], body)).hexdigest(),
}
def _node(signature):
receipt = {
"schema": "szl.killinchu.receipt/v1",
"kind": "contract-test",
"payload": {"bounded": True},
"doctrine": "v11",
"ts_utc": "2026-07-11T00:00:00Z",
}
return receipt, {
"index": 0,
"digest": "a" * 64,
"receipt": receipt,
"dsse": {
"payloadType": "application/vnd.szl.receipt+json",
"signatures": [{"keyid": "test-key", "sig": signature}],
},
}
class ReceiptExportContractTests(unittest.TestCase):
def test_empty_ledger_is_typed_200_with_explicit_limit(self):
body, status = build_receipt_export([], dsse_module=_TestDSSE)
self.assertEqual(status, 200)
self.assertEqual(body["schema"], SCHEMA)
self.assertEqual(body["export_state"], "EMPTY")
self.assertFalse(body["receipt_available"])
self.assertFalse(body["signed"])
self.assertIsNone(body["dsse"])
self.assertEqual(body["verification"]["state"], "NOT_APPLICABLE")
self.assertIn("NO_RECEIPTS", {item["code"] for item in body["limits"]})
def test_valid_signature_is_exported_only_after_verification(self):
receipt, _ = _node("")
sig = _TestDSSE.signature("application/vnd.szl.receipt+json", receipt)
_, node = _node(sig)
body, status = build_receipt_export([node], dsse_module=_TestDSSE, khipu_root=node["digest"])
self.assertEqual(status, 200)
self.assertEqual(body["export_state"], "SIGNED_VERIFIED")
self.assertTrue(body["signed"])
self.assertEqual(body["verification"]["state"], "VERIFIED")
self.assertTrue(body["dsse"]["payload"])
self.assertTrue(body["verify_offline"])
def test_invalid_signature_never_becomes_signed(self):
_, node = _node(base64.b64encode(b"not-a-valid-signature").decode())
body, status = build_receipt_export([node], dsse_module=_TestDSSE)
self.assertEqual(status, 200)
self.assertEqual(body["export_state"], "SIGNATURE_UNVERIFIED")
self.assertFalse(body["signed"])
self.assertEqual(body["verification"]["state"], "FAILED")
self.assertEqual(body["verify_offline"], [])
self.assertIn("NO_VERIFIED_SIGNATURE", {item["code"] for item in body["limits"]})
def test_placeholder_is_not_exposed_as_a_signature(self):
_, node = _node("PLACEHOLDER - not a signature")
node["dsse"]["signatures"][0]["keyid"] = "PENDING"
body, status = build_receipt_export([node], dsse_module=_TestDSSE)
self.assertEqual(status, 200)
self.assertEqual(body["export_state"], "UNSIGNED")
self.assertFalse(body["signed"])
self.assertEqual(body["dsse"]["signatures"], [])
self.assertEqual(body["verification"]["state"], "UNSIGNED")
def test_out_of_range_index_is_rejected_without_fallback(self):
_, node = _node(base64.b64encode(b"x").decode())
body, status = build_receipt_export([node], index=3, dsse_module=_TestDSSE)
self.assertEqual(status, 422)
self.assertFalse(body["ok"])
self.assertEqual(body["export_state"], "INDEX_OUT_OF_RANGE")
self.assertEqual(body["requested_index"], 3)
if __name__ == "__main__":
unittest.main()