ca2a-runtime 0.3.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. ca2a_runtime-0.3.1/.clusterfuzzlite/Dockerfile +5 -0
  2. ca2a_runtime-0.3.1/.clusterfuzzlite/README.md +60 -0
  3. ca2a_runtime-0.3.1/.clusterfuzzlite/build.sh +29 -0
  4. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_attestation_parsers.py +62 -0
  5. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_canonical_json.py +89 -0
  6. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_wire_documents.py +92 -0
  7. ca2a_runtime-0.3.1/.github/dependabot.yml +39 -0
  8. ca2a_runtime-0.3.1/.github/workflows/cflite_batch.yml +36 -0
  9. ca2a_runtime-0.3.1/.github/workflows/cflite_pr.yml +43 -0
  10. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/docker.yml +2 -0
  11. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/require-maintainer-approval.yml +5 -2
  12. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/CHANGELOG.md +21 -1
  13. ca2a_runtime-0.3.1/Dockerfile +40 -0
  14. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/PKG-INFO +1 -1
  15. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/index.md +2 -2
  16. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/pyproject.toml +1 -1
  17. ca2a_runtime-0.3.1/requirements/build.in +6 -0
  18. ca2a_runtime-0.3.1/requirements/build.txt +26 -0
  19. ca2a_runtime-0.3.1/requirements/runtime.in +12 -0
  20. ca2a_runtime-0.3.1/requirements/runtime.txt +620 -0
  21. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/canonical.py +9 -2
  22. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/challenge.py +10 -0
  23. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/credential.py +7 -0
  24. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tdx.py +22 -6
  25. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/server.py +6 -1
  26. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/dag.py +4 -0
  27. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_canonical.py +13 -0
  28. {ca2a_runtime-0.3.0/tests → ca2a_runtime-0.3.1/tests/unit}/test_challenge.py +18 -0
  29. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_container_release.py +25 -3
  30. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_example_rejection_with_proof.py +17 -8
  31. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_live_call.py +29 -0
  32. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_release_artifacts.py +1 -1
  33. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_tdx.py +62 -0
  34. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_trace_binding.py +11 -0
  35. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_verify.py +13 -0
  36. ca2a_runtime-0.3.0/.github/dependabot.yml +0 -23
  37. ca2a_runtime-0.3.0/Dockerfile +0 -30
  38. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.dockerignore +0 -0
  39. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/CODEOWNERS +0 -0
  40. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  42. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  43. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  44. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/actionlint.yml +0 -0
  45. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/ci.yml +0 -0
  46. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/codeql.yml +0 -0
  47. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/contributor-check.yml +0 -0
  48. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/docs.yml +0 -0
  49. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/limitations-parity.yml +0 -0
  50. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/release-drafter.yml +0 -0
  51. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/release.yml +0 -0
  52. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/sbom.yml +0 -0
  53. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/scorecard.yml +0 -0
  54. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/vouch-check.yml +0 -0
  55. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.github/workflows/vouch-command.yml +0 -0
  56. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/.gitignore +0 -0
  57. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/ADOPTERS.md +0 -0
  58. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/CHARTER.md +0 -0
  59. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/CNAME +0 -0
  60. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/CODE_OF_CONDUCT.md +0 -0
  61. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/CONTRIBUTING.md +0 -0
  62. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/GOVERNANCE.md +0 -0
  63. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/LICENSE +0 -0
  64. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/LIMITATIONS.md +0 -0
  65. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/MAINTAINERS.md +0 -0
  66. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/NOTICE +0 -0
  67. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/PRIVACY.md +0 -0
  68. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/README.md +0 -0
  69. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/ROADMAP.md +0 -0
  70. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/SECURITY.md +0 -0
  71. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/SPONSORS.md +0 -0
  72. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/benchmarks/.gitkeep +0 -0
  73. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/SPEC.md +0 -0
  74. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/assets/icon.svg +0 -0
  75. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/assets/og.png +0 -0
  76. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/concepts.md +0 -0
  77. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/configuration.md +0 -0
  78. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/hardware-validation.md +0 -0
  79. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/mutual-hardware-acceptance.md +0 -0
  80. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/quickstart.md +0 -0
  81. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/attestation.md +0 -0
  82. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/call-graph.md +0 -0
  83. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/cedar-policy.md +0 -0
  84. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/component-model.md +0 -0
  85. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/conformance.md +0 -0
  86. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/delegation-chain.md +0 -0
  87. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/error-codes.md +0 -0
  88. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/failure-modes.md +0 -0
  89. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/mutual-attestation.md +0 -0
  90. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/profile.md +0 -0
  91. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/provenance-dag.md +0 -0
  92. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/response-authentication.md +0 -0
  93. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/response-binding-requirements.md +0 -0
  94. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/sealed-channel.md +0 -0
  95. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/threat-model.md +0 -0
  96. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/trace-a2a-profile.md +0 -0
  97. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/transport.md +0 -0
  98. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/spec/verification-library.md +0 -0
  99. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/tutorials/authoring-a-delegation-credential.md +0 -0
  100. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/tutorials/emit-and-verify-provenance.md +0 -0
  101. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/tutorials/integrating-with-a2a.md +0 -0
  102. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/tutorials/reproducing-the-claims.md +0 -0
  103. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/docs/tutorials/verify-a-delegation-chain.md +0 -0
  104. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/a2a-sdk/loopback.py +0 -0
  105. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/a2a-sdk/requirements.txt +0 -0
  106. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/README.md +0 -0
  107. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/ca2a-config.yaml +0 -0
  108. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/chain.json +0 -0
  109. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/dag.json +0 -0
  110. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/demo.py +0 -0
  111. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/policy.cedar +0 -0
  112. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/minimal/README.md +0 -0
  113. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/minimal/ca2a-config.yaml +0 -0
  114. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/minimal/chain.json +0 -0
  115. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/README.md +0 -0
  116. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/chain.json +0 -0
  117. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/dag.json +0 -0
  118. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/demo.py +0 -0
  119. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/trace-dag/README.md +0 -0
  120. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/examples/trace-dag/demo.py +0 -0
  121. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/README.md +0 -0
  122. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim1-attenuation-soundness/README.md +0 -0
  123. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim1-attenuation-soundness/run.py +0 -0
  124. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim2-cross-chain-replay/README.md +0 -0
  125. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim2-cross-chain-replay/run.py +0 -0
  126. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim3-scope-policy-intersection/README.md +0 -0
  127. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim3-scope-policy-intersection/run.py +0 -0
  128. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim4-sealed-payload-confidentiality/README.md +0 -0
  129. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim4-sealed-payload-confidentiality/run.py +0 -0
  130. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim5-provenance-dag-integrity/README.md +0 -0
  131. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim5-provenance-dag-integrity/run.py +0 -0
  132. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim6-cross-operator-attestation/README.md +0 -0
  133. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/claim6-cross-operator-attestation/run.py +0 -0
  134. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/README.md +0 -0
  135. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-attested-peer-azure-2026-07-27/bridge.py +0 -0
  136. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-attested-peer-azure-2026-07-27/transcript.json +0 -0
  137. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/snp_client.py +0 -0
  138. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/tdx_peer.py +0 -0
  139. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/transcript.json +0 -0
  140. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/mutual-snp-2026-09-17/README.md +0 -0
  141. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/mutual-snp-2026-09-17/a-to-b.json +0 -0
  142. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/mutual-snp-2026-09-17/b-to-a.json +0 -0
  143. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/mutual-snp-2026-09-17/controls.py +0 -0
  144. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/governance/ca2a-enforcement.yaml +0 -0
  145. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/hooks/seo.py +0 -0
  146. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/mkdocs.yml +0 -0
  147. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/overrides/main.html +0 -0
  148. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/agt-overrides.txt +0 -0
  149. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/agt.in +0 -0
  150. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/agt.txt +0 -0
  151. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/dev.in +0 -0
  152. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/dev.txt +0 -0
  153. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/docs.txt +0 -0
  154. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/release.in +0 -0
  155. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements/release.txt +0 -0
  156. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/requirements-docs.txt +0 -0
  157. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/robots.txt +0 -0
  158. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/scripts/gen_action_fixtures.py +0 -0
  159. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/scripts/gen_agt_evidence.py +0 -0
  160. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/scripts/gen_example_chain.py +0 -0
  161. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/__init__.py +0 -0
  162. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/agent_manifest.py +0 -0
  163. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/attestation.py +0 -0
  164. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/benchmarks.py +0 -0
  165. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/bootstrap.py +0 -0
  166. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/cedar.py +0 -0
  167. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/channel/__init__.py +0 -0
  168. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/channel/sealed.py +0 -0
  169. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/cli.py +0 -0
  170. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/config.py +0 -0
  171. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/__init__.py +0 -0
  172. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/holder.py +0 -0
  173. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/revocation.py +0 -0
  174. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/errors.py +0 -0
  175. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/hardware_acceptance.py +0 -0
  176. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/node.py +0 -0
  177. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/peer.py +0 -0
  178. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/policy.py +0 -0
  179. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/provenance.py +0 -0
  180. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/response.py +0 -0
  181. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/__init__.py +0 -0
  182. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/base.py +0 -0
  183. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/binding.py +0 -0
  184. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/sev_snp.py +0 -0
  185. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/software.py +0 -0
  186. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tpm.py +0 -0
  187. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tsm.py +0 -0
  188. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/trace_binding.py +0 -0
  189. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/__init__.py +0 -0
  190. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/_b64url.py +0 -0
  191. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/a2a_adapter.py +0 -0
  192. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/a2a_sdk.py +0 -0
  193. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/client.py +0 -0
  194. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/constants.py +0 -0
  195. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/wire.py +0 -0
  196. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/__init__.py +0 -0
  197. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/sev_snp.py +0 -0
  198. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tdx.py +0 -0
  199. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tpm.py +0 -0
  200. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tpm_roots.py +0 -0
  201. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/verify.py +0 -0
  202. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/__init__.py +0 -0
  203. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/committed_blobs.py +0 -0
  204. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conformance/README.md +0 -0
  205. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conformance/__init__.py +0 -0
  206. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conformance/conftest.py +0 -0
  207. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conformance/test_action_fixture_bundles.py +0 -0
  208. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conformance/test_profile_conformance.py +0 -0
  209. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/conftest.py +0 -0
  210. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/a2a/send_message_ca2a.json +0 -0
  211. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/README.md +0 -0
  212. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-001-verified/chain.json +0 -0
  213. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-001-verified/dag.json +0 -0
  214. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-001-verified/expected.json +0 -0
  215. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-002-parent-hash-mismatch/chain.json +0 -0
  216. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-002-parent-hash-mismatch/dag.json +0 -0
  217. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-002-parent-hash-mismatch/expected.json +0 -0
  218. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-006-policy-denial/chain.json +0 -0
  219. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-006-policy-denial/dag.json +0 -0
  220. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-006-policy-denial/expected.json +0 -0
  221. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-010-scope-escalation/chain.json +0 -0
  222. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-010-scope-escalation/dag.json +0 -0
  223. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-010-scope-escalation/expected.json +0 -0
  224. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-012-credential-expired/chain.json +0 -0
  225. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-012-credential-expired/dag.json +0 -0
  226. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/action/action-012-credential-expired/expected.json +0 -0
  227. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/README.md +0 -0
  228. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/ak.pub +0 -0
  229. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/akcert_0x01C101D0.der +0 -0
  230. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/nonce.hex +0 -0
  231. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/pcrs.bin +0 -0
  232. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/quote.msg +0 -0
  233. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/quote.sig +0 -0
  234. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/response-mac-v1.json +0 -0
  235. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/sev_snp/amd_milan_cert_chain.pem +0 -0
  236. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tdx/intel_sgx_root_ca.pem +0 -0
  237. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tpm/bare-rsa-prefix-0016/README.md +0 -0
  238. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tpm/bare-rsa-prefix-0016/ak-chain.pem +0 -0
  239. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tpm/bare-rsa-prefix-0016/attest.hex +0 -0
  240. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tpm/bare-rsa-prefix-0016/signature.hex +0 -0
  241. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/fixtures/tpm/bare-rsa-prefix-0016/trusted-root.pem +0 -0
  242. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/__init__.py +0 -0
  243. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/conftest.py +0 -0
  244. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_a2a_adapter.py +0 -0
  245. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_a2a_sdk_bridge.py +0 -0
  246. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_a2a_sdk_loopback.py +0 -0
  247. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_agent_manifest_cose.py +0 -0
  248. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_bootstrap.py +0 -0
  249. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_cedar.py +0 -0
  250. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_channel_and_tee.py +0 -0
  251. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim1_attenuation.py +0 -0
  252. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim2_replay.py +0 -0
  253. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim3_scope_policy.py +0 -0
  254. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim4_sealed.py +0 -0
  255. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim5_provenance.py +0 -0
  256. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim6_cross_operator.py +0 -0
  257. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_cli_start.py +0 -0
  258. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_committed_examples_verify.py +0 -0
  259. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_config.py +0 -0
  260. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_delegation.py +0 -0
  261. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_dependency_security_floors.py +0 -0
  262. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_docs_quickstart.py +0 -0
  263. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_documented_experiments.py +0 -0
  264. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_errors.py +0 -0
  265. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_hardware_acceptance.py +0 -0
  266. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_holder_binding.py +0 -0
  267. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_lineage_cli.py +0 -0
  268. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_mutual_attestation.py +0 -0
  269. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_peer.py +0 -0
  270. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_peer_request.py +0 -0
  271. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_provenance.py +0 -0
  272. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_response_authentication.py +0 -0
  273. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_revocation.py +0 -0
  274. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_sealed_channel.py +0 -0
  275. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_sev_snp.py +0 -0
  276. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_snp_tdx_attest.py +0 -0
  277. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_tpm.py +0 -0
  278. {ca2a_runtime-0.3.0 → ca2a_runtime-0.3.1}/tests/unit/test_tpm_attest.py +0 -0
@@ -0,0 +1,5 @@
1
+ FROM gcr.io/oss-fuzz-base/base-builder-python
2
+
3
+ COPY . $SRC/ca2a
4
+ COPY .clusterfuzzlite/build.sh $SRC/build.sh
5
+ WORKDIR $SRC/ca2a
@@ -0,0 +1,60 @@
1
+ # Fuzzing
2
+
3
+ Coverage-guided fuzzing via [ClusterFuzzLite](https://google.github.io/clusterfuzzlite/),
4
+ running Atheris against the input cA2A reads before it has decided to trust the
5
+ party that sent it. Mirrors the setup in agentrust-io/agent-manifest and
6
+ agentrust-io/cmcp.
7
+
8
+ ## What is fuzzed
9
+
10
+ | Target | Surface |
11
+ | --- | --- |
12
+ | `fuzz_wire_documents.py` | JSON from outside: the A2A request `PeerNode.handle` serves (delegation chain, holder proof, caller offer, sealed payload), the authenticated-response envelope, a revocation snapshot, and the chain and TRACE DAG documents the offline verifier loads. |
13
+ | `fuzz_attestation_parsers.py` | TDX quote, SEV-SNP report, TPM quote and TPMT_SIGNATURE parsing, plus `verify_challenge` over caller-supplied challenge text. |
14
+ | `fuzz_canonical_json.py` | The RFC 8785 bytes every credential, holder proof, revocation statement and response MAC is computed over. |
15
+
16
+ ## The properties
17
+
18
+ The wire and parser targets assert that each surface fails closed: it returns,
19
+ or raises the error family it documents (`CA2AError`, and `AttestationFailed`
20
+ for evidence). A `TypeError`, `struct.error`, `AttributeError` or
21
+ `RecursionError` escaping means a caller written against the documented
22
+ exception will not catch it, and on the reference server the connection drops
23
+ with no response.
24
+
25
+ `fuzz_canonical_json.py` asserts a round trip: parsing the canonical output must
26
+ reproduce the input exactly, and integers outside +/-(2**53 - 1) must be
27
+ refused. cA2A's data model has no floats, so the comparison is exact.
28
+
29
+ ## Standing when added
30
+
31
+ A local plain-Python run of each `TestOneInput` (Atheris does not install on the
32
+ Windows machine it was written on) found three escapes that are fixed in the
33
+ same change, with regression tests: `TdxQuote.parse` raising `struct.error` on a
34
+ quote whose signature section is shorter than its declared contents,
35
+ `verify_challenge` raising `TypeError` on a non-ASCII MAC, and
36
+ `DelegationCredential.from_dict` raising `TypeError` on a hop that is not an
37
+ object.
38
+
39
+ ## Bundling gotcha
40
+
41
+ `compile_python_fuzzer` bundles each target with PyInstaller, which follows
42
+ static imports only and bundles code, not package data. `build.sh` passes
43
+ `--collect-submodules=email` for the lazy `email.mime` import in the
44
+ cryptography and pydantic stacks, and `--collect-data` for `agentrust_trace`,
45
+ whose TRACE schema `ca2a_verify.dag` loads at import time.
46
+
47
+ ## Running locally
48
+
49
+ ```
50
+ git clone https://github.com/google/clusterfuzzlite --depth 1 /tmp/clusterfuzzlite
51
+ python /tmp/clusterfuzzlite/infra/helper.py build_image --external $PWD
52
+ python /tmp/clusterfuzzlite/infra/helper.py build_fuzzers --external --sanitizer address $PWD
53
+ python /tmp/clusterfuzzlite/infra/helper.py run_fuzzer --external $PWD fuzz_wire_documents
54
+ ```
55
+
56
+ ## In CI
57
+
58
+ `cflite_pr.yml` fuzzes code the pull request touched, for five minutes.
59
+ `cflite_batch.yml` runs every target for thirty minutes, nightly. Both are
60
+ read-only and time out at 45 minutes; the oss-fuzz base image build dominates.
@@ -0,0 +1,29 @@
1
+ #!/bin/bash -eu
2
+ # Build the fuzz targets for ClusterFuzzLite.
3
+ #
4
+ # Installed rather than put on the path so the targets exercise the same import
5
+ # surface a consumer gets. Third-party dependencies come from the same
6
+ # hash-pinned locks the container image uses, and the package goes in with
7
+ # --no-deps and without build isolation, so nothing here resolves unpinned.
8
+
9
+ cd "$SRC/ca2a"
10
+ pip3 install --no-cache-dir --require-hashes -r requirements/build.txt
11
+ pip3 install --no-cache-dir --require-hashes -r requirements/runtime.txt
12
+ pip3 install --no-cache-dir --no-deps --no-build-isolation .
13
+
14
+ # compile_python_fuzzer bundles each target with PyInstaller, which follows
15
+ # static imports only and bundles code, not package data.
16
+ PYI_ARGS=(
17
+ # The cryptography and pydantic stacks reach email.mime lazily; without this
18
+ # the bundled target dies with ModuleNotFoundError, which libFuzzer reports
19
+ # as a crash in the target.
20
+ --collect-submodules=email
21
+ # ca2a_verify.dag validates records against the TRACE JSON schema, which
22
+ # agentrust_trace loads from inside its package at import time.
23
+ --collect-data=agentrust_trace
24
+ --collect-data=agent_manifest
25
+ )
26
+
27
+ for target in "$SRC"/ca2a/.clusterfuzzlite/fuzz_*.py; do
28
+ compile_python_fuzzer "$target" "${PYI_ARGS[@]}"
29
+ done
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the attestation evidence parsers.
3
+
4
+ A callee reads a caller's evidence (``raw_evidence`` in a ``caller_offer``)
5
+ before deciding to trust anything about it, and an auditor feeds recorded
6
+ quotes to ``ca2a_verify`` offline. These parsers read that binary at fixed
7
+ offsets under declared lengths the blob itself supplies: a TDX quote with four
8
+ nested size fields, an SNP report, a TPM quote and a TPMT_SIGNATURE.
9
+
10
+ The property is that each fails closed: it returns, or raises
11
+ AttestationFailed, the error the verifier documents. A struct.error, IndexError
12
+ or OverflowError reaching the caller means a declared length was believed. That
13
+ is the bug this target was written against: TdxQuote.parse used to raise
14
+ struct.error on a quote whose signature section was shorter than it claimed.
15
+
16
+ The challenge verifier is included because its input, the offer nonce and the
17
+ holder proof's challenge, is caller text read before anything authenticates it.
18
+ """
19
+
20
+ import sys
21
+
22
+ import atheris
23
+
24
+ with atheris.instrument_imports():
25
+ from ca2a_runtime.challenge import verify_challenge
26
+ from ca2a_runtime.errors import AttestationFailed
27
+ from ca2a_runtime.tee.sev_snp import SevSnpReport
28
+ from ca2a_runtime.tee.tdx import TdxQuote
29
+ from ca2a_runtime.tee.tpm import TpmQuote
30
+ from ca2a_verify.tpm import parse_tpmt_signature
31
+
32
+ _SECRET = b"\x00" * 32
33
+
34
+ _PARSERS = [
35
+ TdxQuote.parse,
36
+ SevSnpReport.parse,
37
+ TpmQuote.parse,
38
+ parse_tpmt_signature,
39
+ ]
40
+
41
+
42
+ def TestOneInput(data: bytes) -> None:
43
+ if not data:
44
+ return
45
+ fdp = atheris.FuzzedDataProvider(data)
46
+ choice = fdp.ConsumeIntInRange(0, len(_PARSERS))
47
+ try:
48
+ if choice == len(_PARSERS):
49
+ verify_challenge(_SECRET, fdp.ConsumeUnicode(fdp.remaining_bytes()))
50
+ else:
51
+ _PARSERS[choice](fdp.ConsumeBytes(fdp.remaining_bytes()))
52
+ except AttestationFailed:
53
+ pass
54
+
55
+
56
+ def main() -> None:
57
+ atheris.Setup(sys.argv, TestOneInput)
58
+ atheris.Fuzz()
59
+
60
+
61
+ if __name__ == "__main__":
62
+ main()
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the RFC 8785 canonicalizer for the invariant its signatures depend on.
3
+
4
+ canonicalize() produces the bytes every delegation credential, holder proof,
5
+ revocation statement and response MAC is computed over, so the bytes must be a
6
+ faithful, lossless encoding of the input. The property asserted is a round trip:
7
+ parsing the canonical output back must reproduce the input exactly.
8
+
9
+ A crash-only check would pass over the bugs that matter here. The ones found in
10
+ sibling canonicalizers were silent: two distinct keys serialized as one key
11
+ twice, a parser kept one of the pair, and a field vanished from a document whose
12
+ signature claimed to cover it.
13
+
14
+ Structure is built from the fuzz data rather than by mutating JSON text, so the
15
+ budget goes on key and value shapes (combining marks, surrogate pairs, control
16
+ characters, integers straddling 2**53) rather than on producing valid JSON.
17
+ """
18
+
19
+ import json
20
+ import sys
21
+
22
+ import atheris
23
+
24
+ with atheris.instrument_imports():
25
+ from ca2a_runtime.canonical import canonicalize
26
+
27
+ _MAX_DEPTH = 4
28
+ _MAX_ITEMS = 6
29
+ _SAFE = 2**53 - 1
30
+
31
+
32
+ def _build(fdp, depth=0):
33
+ if depth >= _MAX_DEPTH or fdp.remaining_bytes() == 0:
34
+ return fdp.ConsumeUnicodeNoSurrogates(16)
35
+ kind = fdp.ConsumeIntInRange(0, 6)
36
+ if kind == 0:
37
+ return None
38
+ if kind == 1:
39
+ return fdp.ConsumeBool()
40
+ if kind == 2:
41
+ # Straddle the safe-integer boundary: outside it canonicalize() must
42
+ # refuse rather than emit digits a conforming verifier would not.
43
+ return fdp.ConsumeIntInRange(-(2**54), 2**54)
44
+ if kind == 3:
45
+ return fdp.ConsumeUnicodeNoSurrogates(64)
46
+ if kind == 4:
47
+ return [_build(fdp, depth + 1) for _ in range(fdp.ConsumeIntInRange(0, _MAX_ITEMS))]
48
+ return {
49
+ fdp.ConsumeUnicodeNoSurrogates(24): _build(fdp, depth + 1)
50
+ for _ in range(fdp.ConsumeIntInRange(0, _MAX_ITEMS))
51
+ }
52
+
53
+
54
+ def _has_unsafe_int(value):
55
+ if isinstance(value, bool):
56
+ return False
57
+ if isinstance(value, int):
58
+ return not -_SAFE <= value <= _SAFE
59
+ if isinstance(value, dict):
60
+ return any(_has_unsafe_int(v) for v in value.values())
61
+ if isinstance(value, list):
62
+ return any(_has_unsafe_int(v) for v in value)
63
+ return False
64
+
65
+
66
+ def TestOneInput(data: bytes) -> None:
67
+ fdp = atheris.FuzzedDataProvider(data)
68
+ value = _build(fdp)
69
+ try:
70
+ out = canonicalize(value)
71
+ except ValueError:
72
+ # Declared: integers outside the RFC 8785 safe domain, and only those.
73
+ assert _has_unsafe_int(value), "canonicalize() refused an in-domain value"
74
+ return
75
+ assert not _has_unsafe_int(value), "an out-of-domain integer was serialized"
76
+
77
+ # cA2A's data model has no floats, so every number re-parses as the int it
78
+ # was and the round trip can be asserted exactly, bools included.
79
+ assert json.loads(out) == value, f"canonical bytes did not round-trip: {out!r}"
80
+ assert canonicalize(json.loads(out)) == out, "canonical form is not a fixed point"
81
+
82
+
83
+ def main() -> None:
84
+ atheris.Setup(sys.argv, TestOneInput)
85
+ atheris.Fuzz()
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the JSON documents cA2A accepts from outside.
3
+
4
+ Each surface takes arbitrary JSON from a party that has not been authenticated
5
+ yet, and each documents one error family for anything malformed:
6
+
7
+ - the A2A request a callee serves (``PeerNode.handle``), which parses the
8
+ delegation chain, holder proof, caller offer and sealed payload, then runs
9
+ the inbound pipeline up to the first refusal: CA2AError;
10
+ - the authenticated-response envelope (``response.decode``):
11
+ ResponseAuthenticationFailed, a CA2AError;
12
+ - a revocation snapshot (``RevocationSnapshot.from_dict``): InvalidRevocation;
13
+ - a delegation chain document and a TRACE DAG handed to the offline verifier:
14
+ CA2AError.
15
+
16
+ Anything else escaping (TypeError, AttributeError, KeyError, UnicodeError,
17
+ RecursionError) is a crash: on the live path it drops the connection with no
18
+ response, and offline it escapes the verifier's contract. The input is decoded
19
+ the way the reference server decodes a body, so what reaches the parsers is
20
+ exactly what the wire can deliver.
21
+ """
22
+
23
+ import contextlib
24
+ import json
25
+ import sys
26
+
27
+ import atheris
28
+
29
+ with atheris.instrument_imports():
30
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
31
+
32
+ from ca2a_runtime import response
33
+ from ca2a_runtime.delegation import RevocationSnapshot
34
+ from ca2a_runtime.errors import CA2AError
35
+ from ca2a_runtime.node import PeerNode
36
+ from ca2a_runtime.policy import LocalPolicy
37
+ from ca2a_verify.dag import verify_trace_dag
38
+ from ca2a_verify.verify import _parse_chain, verify_delegation_chain
39
+
40
+ _ROOT = Ed25519PrivateKey.from_private_bytes(b"\x01" * 32).public_key().public_bytes_raw().hex()
41
+ _NODE = PeerNode(LocalPolicy.of(["read", "write"]), trusted_root_issuers={_ROOT})
42
+
43
+
44
+ def _handle(doc):
45
+ if isinstance(doc, dict):
46
+ _NODE.handle(doc)
47
+
48
+
49
+ def _revocations(doc):
50
+ RevocationSnapshot.from_dict(doc)
51
+
52
+
53
+ def _chain(doc):
54
+ verify_delegation_chain(_parse_chain(doc), trusted_root_issuers={_ROOT})
55
+
56
+
57
+ def _dag(doc):
58
+ if isinstance(doc, dict):
59
+ doc = doc.get("records", doc)
60
+ if isinstance(doc, list) and doc:
61
+ verify_trace_dag(doc, trusted_keys=[])
62
+
63
+
64
+ _JSON_TARGETS = [_handle, _revocations, _chain, _dag]
65
+
66
+
67
+ def TestOneInput(data: bytes) -> None:
68
+ if not data:
69
+ return
70
+ fdp = atheris.FuzzedDataProvider(data)
71
+ choice = fdp.ConsumeIntInRange(0, len(_JSON_TARGETS))
72
+ raw = fdp.ConsumeBytes(fdp.remaining_bytes())
73
+ if choice == len(_JSON_TARGETS):
74
+ with contextlib.suppress(CA2AError):
75
+ response.decode(raw)
76
+ return
77
+ try:
78
+ doc = json.loads(raw)
79
+ except (ValueError, RecursionError):
80
+ # The reference server answers 400 before any parser runs.
81
+ return
82
+ with contextlib.suppress(CA2AError):
83
+ _JSON_TARGETS[choice](doc)
84
+
85
+
86
+ def main() -> None:
87
+ atheris.Setup(sys.argv, TestOneInput)
88
+ atheris.Fuzz()
89
+
90
+
91
+ if __name__ == "__main__":
92
+ main()
@@ -0,0 +1,39 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ open-pull-requests-limit: 5
9
+ ignore:
10
+ - dependency-name: "*"
11
+ update-types:
12
+ - "version-update:semver-major"
13
+
14
+ - package-ecosystem: "github-actions"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "weekly"
18
+ day: "monday"
19
+ open-pull-requests-limit: 5
20
+ groups:
21
+ codeql-action:
22
+ patterns:
23
+ - "github/codeql-action*"
24
+
25
+ # Refreshes the digest the Dockerfile pins its base image to. Without this the
26
+ # pin would hold the image at whatever it was on the day it was written.
27
+ - package-ecosystem: "docker"
28
+ directory: "/"
29
+ schedule:
30
+ interval: "weekly"
31
+ day: "monday"
32
+ open-pull-requests-limit: 5
33
+ # Digest refreshes and 3.11 patch releases only. Moving the image to a new
34
+ # Python minor is a deliberate change with its own PR and test run.
35
+ ignore:
36
+ - dependency-name: "python"
37
+ update-types:
38
+ - "version-update:semver-major"
39
+ - "version-update:semver-minor"
@@ -0,0 +1,36 @@
1
+ name: ClusterFuzzLite batch
2
+
3
+ on:
4
+ schedule:
5
+ # 04:40 UTC daily, off the hour and off agent-manifest's 04:20 slot.
6
+ - cron: '40 4 * * *'
7
+ workflow_dispatch:
8
+
9
+ permissions: read-all
10
+
11
+ concurrency:
12
+ group: cflite-batch
13
+ cancel-in-progress: false
14
+
15
+ jobs:
16
+ fuzz:
17
+ name: Batch fuzz
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 45
20
+ steps:
21
+ - name: Build fuzzers
22
+ uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
23
+ with:
24
+ language: python
25
+ github-token: ${{ secrets.GITHUB_TOKEN }}
26
+ sanitizer: address
27
+
28
+ - name: Run fuzzers
29
+ uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
30
+ with:
31
+ github-token: ${{ secrets.GITHUB_TOKEN }}
32
+ # Every target, not just what changed. Thirty minutes of fuzzing
33
+ # leaves room for the base image build inside the 45 minute timeout.
34
+ fuzz-seconds: 1800
35
+ mode: batch
36
+ sanitizer: address
@@ -0,0 +1,43 @@
1
+ name: ClusterFuzzLite PR
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - 'src/**'
7
+ - 'requirements/build.txt'
8
+ - 'requirements/runtime.txt'
9
+ - '.clusterfuzzlite/**'
10
+ - '.github/workflows/cflite_pr.yml'
11
+
12
+ # Read-only. Findings surface in the job log and the uploaded crash artifact
13
+ # rather than as code-scanning alerts, so no security-events: write is needed.
14
+ permissions: read-all
15
+
16
+ concurrency:
17
+ group: cflite-pr-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ fuzz:
22
+ name: Fuzz changed code
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 45
25
+ steps:
26
+ # Only the address sanitizer. The targets are pure Python under Atheris,
27
+ # where undefined-behaviour instrumentation has nothing to instrument.
28
+ - name: Build fuzzers
29
+ uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
30
+ with:
31
+ language: python
32
+ github-token: ${{ secrets.GITHUB_TOKEN }}
33
+ sanitizer: address
34
+
35
+ - name: Run fuzzers
36
+ uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
37
+ with:
38
+ github-token: ${{ secrets.GITHUB_TOKEN }}
39
+ fuzz-seconds: 300
40
+ # code-change fuzzes only what the PR touched, which is what keeps
41
+ # this inside a PR's time budget. The nightly batch covers the rest.
42
+ mode: code-change
43
+ sanitizer: address
@@ -9,6 +9,8 @@ on:
9
9
  - "Dockerfile"
10
10
  - ".dockerignore"
11
11
  - "pyproject.toml"
12
+ - "requirements/build.txt"
13
+ - "requirements/runtime.txt"
12
14
  - "src/**"
13
15
  - ".github/workflows/docker.yml"
14
16
 
@@ -13,12 +13,15 @@ on:
13
13
 
14
14
  permissions:
15
15
  contents: read
16
- pull-requests: read
17
- statuses: write
18
16
 
19
17
  jobs:
20
18
  gate:
21
19
  runs-on: ubuntu-latest
20
+ # Reads the PR and its reviews, then fails the job if no maintainer
21
+ # approved the head commit. It writes nothing, so no write scope.
22
+ permissions:
23
+ contents: read
24
+ pull-requests: read
22
25
  if: github.event.pull_request.base.ref == 'main'
23
26
  steps:
24
27
  - name: Check for maintainer approval
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.1] - 2026-09-26
11
+
12
+ ### Fixed
13
+
14
+ - Malformed input from outside now fails with the documented error instead of
15
+ escaping as a Python exception. `TdxQuote.parse` confines reads to the declared
16
+ signature section and checks each nested length (it raised `struct.error` on a
17
+ short section); `verify_challenge` refuses a MAC that is not 64 lowercase hex
18
+ (a non-ASCII one raised `TypeError`); `DelegationCredential.from_dict` and
19
+ `verify_trace_dag` refuse entries that are not objects; the reference server
20
+ answers 400 to bodies that hit the JSON parser's nesting or integer-digit
21
+ limits instead of dropping the connection (#202).
22
+ - `canonicalize` refuses non-string object keys. It coerced them with `str()`, so
23
+ `{1: "a", "1": "b"}` serialized one key twice (#202).
24
+ - The container image installs its dependencies from hash-pinned locks
25
+ (`requirements/runtime.txt`, `requirements/build.txt`) and pins the base image
26
+ by digest. ClusterFuzzLite targets cover the wire documents, the attestation
27
+ parsers and the canonicalizer (#202).
28
+
10
29
  ## [0.3.0] - 2026-09-25
11
30
 
12
31
  ### Changed
@@ -391,7 +410,8 @@ built/stubbed boundary.
391
410
  - The sealed channel does not by itself establish the enclave-held-private-key property; that is a hardware attestation guarantee that lands with real-hardware validation.
392
411
  - Alpha schemas: the delegation credential and TRACE link schemas are not yet stable or versioned, and peer attestation evidence is not yet RATS/EAT conformant.
393
412
 
394
- [Unreleased]: https://github.com/agentrust-io/ca2a/compare/v0.3.0...main
413
+ [Unreleased]: https://github.com/agentrust-io/ca2a/compare/v0.3.1...main
414
+ [0.3.1]: https://github.com/agentrust-io/ca2a/compare/v0.3.0...v0.3.1
395
415
  [0.3.0]: https://github.com/agentrust-io/ca2a/compare/v0.2.0...v0.3.0
396
416
  [0.2.0]: https://github.com/agentrust-io/ca2a/compare/v0.1.0a1...v0.2.0
397
417
  [0.1.0a1]: https://github.com/agentrust-io/ca2a/releases/tag/v0.1.0a1
@@ -0,0 +1,40 @@
1
+ # Base image pinned by its multi-arch index digest, so a re-pushed tag cannot
2
+ # change what is built. Refresh the digest and the tag together.
3
+ FROM python:3.11.15-slim-bookworm@sha256:d29f48a31a8b408ed19272ca1e7b10ebae13b240a27e862d3d4217c528e2e0c3 AS builder
4
+
5
+ ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
6
+ PIP_NO_CACHE_DIR=1
7
+
8
+ WORKDIR /build
9
+ COPY requirements/build.txt ./requirements/build.txt
10
+ # The build backend comes from a hash-pinned lock, and the wheel is built
11
+ # without build isolation so pip does not fetch an unpinned hatchling of its own.
12
+ RUN python -m pip install --require-hashes -r requirements/build.txt
13
+ COPY pyproject.toml README.md LICENSE NOTICE ./
14
+ COPY src ./src
15
+ RUN python -m pip wheel --no-deps --no-build-isolation --wheel-dir /wheels .
16
+
17
+ FROM python:3.11.15-slim-bookworm@sha256:d29f48a31a8b408ed19272ca1e7b10ebae13b240a27e862d3d4217c528e2e0c3 AS runtime
18
+
19
+ ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
20
+ PIP_NO_CACHE_DIR=1 \
21
+ PYTHONUNBUFFERED=1
22
+
23
+ RUN groupadd --gid 10001 ca2a \
24
+ && useradd --uid 10001 --gid 10001 --no-create-home --home-dir /var/lib/ca2a ca2a \
25
+ && install -d -o ca2a -g ca2a /var/lib/ca2a /etc/ca2a
26
+
27
+ # Third-party runtime dependencies from the hash-pinned lock, then the locally
28
+ # built wheel with --no-deps, so nothing in the image is resolved unpinned.
29
+ COPY requirements/runtime.txt /tmp/runtime.txt
30
+ COPY --from=builder /wheels /wheels
31
+ RUN python -m pip install --require-hashes -r /tmp/runtime.txt \
32
+ && python -m pip install --no-deps /wheels/ca2a_runtime-*.whl \
33
+ && rm -rf /wheels /tmp/runtime.txt
34
+
35
+ USER 10001:10001
36
+ WORKDIR /var/lib/ca2a
37
+ EXPOSE 8443
38
+
39
+ ENTRYPOINT ["ca2a"]
40
+ CMD ["--help"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ca2a-runtime
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Confidential agent-to-agent runtime: attested, attenuated delegation and sealed peer channels for A2A
5
5
  Project-URL: Homepage, https://github.com/agentrust-io/ca2a
6
6
  Project-URL: Repository, https://github.com/agentrust-io/ca2a
@@ -16,7 +16,7 @@ cA2A is a profile on A2A that adds signed attenuated delegation, peer appraisal,
16
16
  [What this proves, and what it does not](LIMITATIONS.md){ .md-button }
17
17
 
18
18
  !!! tip "TL;DR"
19
- [ca2a-runtime](https://pypi.org/project/ca2a-runtime/) 0.3.0 (MIT developer preview; the PyPI name `ca2a` belongs to an unrelated project) rejects scope escalation offline with no hardware or running peer. SEV-SNP and TDX appraisal ran on real Azure and GCP evidence, including an Azure SEV-SNP peer calling a GCP TDX peer on 2026-07-27, but peer appraisal is one-directional so far and binding the seal to a verified measurement on a live call is on the roadmap.
19
+ [ca2a-runtime](https://pypi.org/project/ca2a-runtime/) 0.3.1 (MIT developer preview; the PyPI name `ca2a` belongs to an unrelated project) rejects scope escalation offline with no hardware or running peer. SEV-SNP and TDX appraisal ran on real Azure and GCP evidence, including an Azure SEV-SNP peer calling a GCP TDX peer on 2026-07-27, but peer appraisal is one-directional so far and binding the seal to a verified measurement on a live call is on the roadmap.
20
20
 
21
21
  <div class="grid cards" markdown>
22
22
 
@@ -71,4 +71,4 @@ Software and hardware modes provide different assurance. Peer appraisal has been
71
71
 
72
72
  For the architecture and trust boundaries, read [How It Works](docs/concepts.md). The normative profile, with the delegation chain, sealed channel, and conformance rules, is in [Profile](docs/spec/profile.md).
73
73
 
74
- **Status:** ca2a-runtime 0.3.0 developer preview · MIT · hosting at the Agentic AI Foundation proposed, not accepted · Sponsored by OPAQUE, which funds the engineering, infrastructure and confidential-computing work behind these projects.
74
+ **Status:** ca2a-runtime 0.3.1 developer preview · MIT · hosting at the Agentic AI Foundation proposed, not accepted · Sponsored by OPAQUE, which funds the engineering, infrastructure and confidential-computing work behind these projects.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ca2a-runtime"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Confidential agent-to-agent runtime: attested, attenuated delegation and sealed peer channels for A2A"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,6 @@
1
+ # Build backend for the container image's builder stage. The wheel is built with
2
+ # --no-build-isolation so pip does not fetch hatchling unpinned into a throwaway
3
+ # environment. Compile with:
4
+ # uv pip compile requirements/build.in --generate-hashes --universal \
5
+ # --python-version 3.11 -o requirements/build.txt
6
+ hatchling
@@ -0,0 +1,26 @@
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile requirements/build.in --generate-hashes --universal --python-version 3.11 -o requirements/build.txt
3
+ hatchling==1.32.4 \
4
+ --hash=sha256:08ecf7548fb48205e7f213d70c71e67b8271b7242093dc3f1da578b42c734a2c \
5
+ --hash=sha256:c4468f73144c054d2aab4ef0f0378c43b9878bf07f8ffd6b79690e970d375f07
6
+ # via -r requirements/build.in
7
+ packaging==26.3 \
8
+ --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \
9
+ --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c
10
+ # via hatchling
11
+ pathspec==1.1.1 \
12
+ --hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \
13
+ --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189
14
+ # via hatchling
15
+ pluggy==1.6.0 \
16
+ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \
17
+ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
18
+ # via hatchling
19
+ tomlkit==0.15.1 \
20
+ --hash=sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304 \
21
+ --hash=sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97
22
+ # via hatchling
23
+ trove-classifiers==2026.9.21.13 \
24
+ --hash=sha256:0a9ebc8d4e2f3e8a22848c5258033035bec17a3012ac3fea16dbaa764489eb71 \
25
+ --hash=sha256:8b1ff4f9c191b1040b71c37f1e445ab99732911e3cd91de52838453a854d7a17
26
+ # via hatchling
@@ -0,0 +1,12 @@
1
+ # Runtime dependencies for the container image, mirroring [project.dependencies]
2
+ # in pyproject.toml. The Dockerfile installs this lock with --require-hashes and
3
+ # then adds the locally built ca2a-runtime wheel with --no-deps, so nothing in
4
+ # the image is fetched unpinned. Keep it in step with pyproject.toml. Compile with:
5
+ # uv pip compile requirements/runtime.in --generate-hashes --universal \
6
+ # --python-version 3.11 -o requirements/runtime.txt
7
+ cryptography>=50.0
8
+ pyyaml>=6.0
9
+ cedarpy==4.8.7
10
+ agentrust-trace>=0.5
11
+ agent-manifest>=0.11.1
12
+ rfc8785>=0.1