vouch-protocol 2.2.1__tar.gz → 3.0.0__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.
- {vouch_protocol-2.2.1/vouch_protocol.egg-info → vouch_protocol-3.0.0}/PKG-INFO +1 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/pyproject.toml +1 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_authority_state.py +32 -17
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_autosign.py +22 -6
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_caveats.py +121 -0
- vouch_protocol-3.0.0/tests/test_ccg_demo.py +29 -0
- vouch_protocol-3.0.0/tests/test_delegation_chain_vectors.py +43 -0
- vouch_protocol-3.0.0/tests/test_delegation_verify.py +471 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_dx_sugar.py +64 -2
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_fleet.py +62 -2
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_heartbeat.py +57 -48
- vouch_protocol-3.0.0/tests/test_heartbeat_hardening.py +207 -0
- vouch_protocol-3.0.0/tests/test_mcp_check_trust.py +81 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_merkle.py +4 -4
- vouch_protocol-3.0.0/tests/test_policy_limits.py +134 -0
- vouch_protocol-3.0.0/tests/test_pq_verify.py +155 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_quorum.py +36 -9
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_signer_vc.py +21 -10
- vouch_protocol-3.0.0/tests/test_trust_check.py +277 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_validator_server.py +70 -11
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_verifier_vc.py +8 -2
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/__init__.py +1 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/attenuation.py +117 -5
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/caveats.py +117 -7
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/conformance.py +74 -41
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/did_web.py +28 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/fleet.py +31 -16
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/heartbeat.py +198 -13
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/mcp/server.py +17 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/merkle.py +41 -17
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/quorum.py +2 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/signer.py +87 -7
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/trust_check.py +141 -28
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/trust_entropy.py +83 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/validator_server.py +29 -2
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/vc.py +31 -2
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/verifier.py +377 -30
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0/vouch_protocol.egg-info}/PKG-INFO +1 -1
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/SOURCES.txt +7 -0
- vouch_protocol-2.2.1/tests/test_trust_check.py +0 -114
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/LICENSE +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/MANIFEST.in +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/README.md +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/setup.cfg +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_accountability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_agent_enroll.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_attribution.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_audio_bridge.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_audit_trail.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_auditor.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_authority_state_vectors.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_behavioral_attestation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_bridge.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_budget.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_c2pa_formats.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_cache.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_canary.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_cli_init.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_cli_root.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_conformance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_credential_gate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_data_integrity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_delegation_attenuation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_deliberation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_disconnected_edge.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_dtn_pads.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_examples_robotics.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_fastapi_credential_gate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_git_workflow.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_grade.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_halos_evidence.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_hardware_seam.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_hasura_integration.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_hybrid.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_hybrid_interop.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_intent_recheck.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_jcs.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_jcs_interop.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_liveness_conformance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_mcp_server_shield.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_media_badge.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_media_native.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_mint_contributor_credential.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_multikey.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_nonce.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_orbital_accumulator.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_proof_of_integration.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_provenance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reasoning.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_receipts.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_recovery.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation_aggregate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation_disputes.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation_ledger.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation_policy.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_reputation_portability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_revocation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_access.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_conformance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_consent.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_custody.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_embodiment.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_fusion.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_handover.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_handshake_blackbox_passport.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_identity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_lease_quorum.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_lifecycle.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_liveness_revocation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_odd.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_perception.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_pq.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_provenance_capability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_root_identity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_safety_record.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_swarm.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_teleop.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_robot_wear.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_root_of_trust.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_root_of_trust_vectors.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_ros2_action_gate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_runtime_vectors.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_scan.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_secure_key_custody.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_security_hardening.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_shield_guard.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_shield_rules.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_shield_vectors.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_status_list.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_threshold.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_threshold_loader.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_tool_registry.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_transparency.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_transport.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_transport_http_rendezvous.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_transport_rendezvous.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_transport_udna_sdk.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_trifecta.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_trust_entropy.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_vc.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/tests/test_vouch_mcp.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/accountability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/agent.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/async_verifier.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/attribution.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/attribution_cli.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/audio.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/audit_trail.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/auditor.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/authority_state.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/autosign.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/behavioral_attestation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/bridge/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/bridge/audio_routes.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/bridge/config.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/bridge/server.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/budget.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/cache.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/canary.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/cli.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/config.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/credential.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/data_integrity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/data_integrity_hybrid.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/deliberation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/gate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/grade.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/_common.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/a2a.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/adk.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/amnesia.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/autogen/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/autogpt/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/autogpt/commands.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/claude-code/settings.hooks.json +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/crewai/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/crewai/tool.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/fastapi.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/google.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/goose.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/hasura/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/hasura/webhook.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/langchain/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/langchain/tool.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/langgraph.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/mcp/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/mlflow.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/n8n.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/openai.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/safetensors.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/streamlit/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/streamlit/seal.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/integrations/vertex_ai/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/intent_recheck.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/jcs.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/keys.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/keystore.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/kms.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/liveness_conformance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/mcp/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/mcp/_guard.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/mcp_guard.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/media/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/media/badge.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/media/c2pa.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/media/native.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/metrics.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/multikey.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/nonce.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/onboard.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/proof_of_integration.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/provenance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/ratelimit.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reasoning.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/receipts.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/recovery.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/registry.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation_aggregate.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation_disputes.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation_ledger.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation_policy.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/reputation_portability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/revocation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/_signing.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/_verify.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/access.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/accumulator.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/blackbox.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/bundle.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/capability.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/conformance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/consent.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/custody.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/dtn_revocation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/edge_trust.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/embodiment.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/freshness.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/fusion.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/geoscope.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/halos.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/handover.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/handshake.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/hardware.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/identity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/lease.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/lifecycle.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/liveness.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/localization.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/odd.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/orbital.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/passport.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/perception.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/perception_consensus.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/physical_quorum.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/pq.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/presence.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/provenance.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/quorum_trust.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/revocation.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/root_identity.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/safety_record.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/swarm.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/teleop.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/robotics/wear.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/root_of_trust.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/scan/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/scan/detector.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/scan/patterns.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/seal.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/demo.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/flight_recorder.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/rules.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/shield.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/shield/trust_registry.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/ssrf.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/status_list.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/status_list_fetcher.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/threshold.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/tool_registry.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transparency.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/__init__.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/base.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/did_key.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/envelope.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/http_rendezvous.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/http_transport.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/manager.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/rendezvous.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/transport/udna.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/trifecta.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch/verification.py +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/dependency_links.txt +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/entry_points.txt +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/requires.txt +0 -0
- {vouch_protocol-2.2.1 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/top_level.txt +0 -0
|
@@ -37,6 +37,7 @@ from vouch.status_list import (
|
|
|
37
37
|
from vouch.heartbeat import HeartbeatRequest, HeartbeatSession, HeartbeatValidator
|
|
38
38
|
from vouch.vc import build_session_voucher
|
|
39
39
|
from vouch.trust_check import verify_agent_call
|
|
40
|
+
from _heartbeat_keys import resolve, signed_session # noqa: E402
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
AUTHORITY_DID = "did:web:treasury.example.com"
|
|
@@ -312,7 +313,7 @@ def test_session_voucher_carries_epoch():
|
|
|
312
313
|
|
|
313
314
|
|
|
314
315
|
def test_heartbeat_request_roundtrips_epoch():
|
|
315
|
-
session =
|
|
316
|
+
session = signed_session("did:web:agent.example.com", authority_epoch=6)
|
|
316
317
|
req = session.build_request()
|
|
317
318
|
assert req.authority_epoch == 6
|
|
318
319
|
d = req.to_dict()
|
|
@@ -322,8 +323,8 @@ def test_heartbeat_request_roundtrips_epoch():
|
|
|
322
323
|
|
|
323
324
|
|
|
324
325
|
def test_heartbeat_validator_tracks_highest_epoch_and_mints_voucher():
|
|
325
|
-
validator = HeartbeatValidator(validator_did="did:web:v.example.com")
|
|
326
|
-
session =
|
|
326
|
+
validator = HeartbeatValidator(key_resolver=resolve, validator_did="did:web:v.example.com")
|
|
327
|
+
session = signed_session("did:web:agent.example.com", authority_epoch=2)
|
|
327
328
|
res = validator.validate(session.build_request().to_dict())
|
|
328
329
|
assert res.ok
|
|
329
330
|
assert res.session_voucher["credentialSubject"]["authorityEpoch"] == 2
|
|
@@ -349,21 +350,29 @@ def agent_call():
|
|
|
349
350
|
intent = {"action": "transfer", "target": "vendor-1", "resource": "https://bank.example.com/x"}
|
|
350
351
|
cred = signer.sign(intent=intent, valid_seconds=300)
|
|
351
352
|
# A voucher minted under authority epoch 5, still well within its time window.
|
|
352
|
-
voucher
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
353
|
+
# The voucher is signed by its validator; an unsigned voucher never counts.
|
|
354
|
+
from vouch.vc import sign_session_voucher
|
|
355
|
+
|
|
356
|
+
v_ident = generate_identity(domain="v.example.com")
|
|
357
|
+
v_signer = Signer(private_key=v_ident.private_key_jwk, did=v_ident.did)
|
|
358
|
+
voucher = sign_session_voucher(
|
|
359
|
+
build_session_voucher(
|
|
360
|
+
subject_did=ident.did,
|
|
361
|
+
validator_dids=[v_signer.did],
|
|
362
|
+
decay_lambda=0.001,
|
|
363
|
+
initial_trust=1.0,
|
|
364
|
+
max_ttl_seconds=3600,
|
|
365
|
+
scope=["agent_actions"],
|
|
366
|
+
valid_seconds=300,
|
|
367
|
+
authority_epoch=5,
|
|
368
|
+
),
|
|
369
|
+
v_signer,
|
|
361
370
|
)
|
|
362
|
-
return signer, cred, voucher
|
|
371
|
+
return signer, cred, voucher, v_signer.get_public_key_multikey()
|
|
363
372
|
|
|
364
373
|
|
|
365
374
|
def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
|
|
366
|
-
signer, cred, voucher = agent_call
|
|
375
|
+
signer, cred, voucher, vkey = agent_call
|
|
367
376
|
pub = signer.get_public_key_multikey()
|
|
368
377
|
|
|
369
378
|
# Routine: time-decay only. The voucher is time-valid, so the call passes.
|
|
@@ -371,6 +380,7 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
|
|
|
371
380
|
cred,
|
|
372
381
|
public_key=pub,
|
|
373
382
|
session_voucher=voucher,
|
|
383
|
+
voucher_public_key=vkey,
|
|
374
384
|
trust_threshold=0.5,
|
|
375
385
|
consequence=CONSEQUENCE_ROUTINE,
|
|
376
386
|
)
|
|
@@ -382,6 +392,7 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
|
|
|
382
392
|
cred,
|
|
383
393
|
public_key=pub,
|
|
384
394
|
session_voucher=voucher,
|
|
395
|
+
voucher_public_key=vkey,
|
|
385
396
|
trust_threshold=0.5,
|
|
386
397
|
consequence=CONSEQUENCE_SENSITIVE,
|
|
387
398
|
last_seen_authority_epoch=7,
|
|
@@ -394,12 +405,13 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
|
|
|
394
405
|
|
|
395
406
|
|
|
396
407
|
def test_sensitive_passes_when_epoch_current(agent_call):
|
|
397
|
-
signer, cred, voucher = agent_call
|
|
408
|
+
signer, cred, voucher, vkey = agent_call
|
|
398
409
|
pub = signer.get_public_key_multikey()
|
|
399
410
|
v = verify_agent_call(
|
|
400
411
|
cred,
|
|
401
412
|
public_key=pub,
|
|
402
413
|
session_voucher=voucher,
|
|
414
|
+
voucher_public_key=vkey,
|
|
403
415
|
trust_threshold=0.5,
|
|
404
416
|
consequence=CONSEQUENCE_SENSITIVE,
|
|
405
417
|
last_seen_authority_epoch=5,
|
|
@@ -409,13 +421,14 @@ def test_sensitive_passes_when_epoch_current(agent_call):
|
|
|
409
421
|
|
|
410
422
|
|
|
411
423
|
def test_critical_requires_live_cosign(agent_call):
|
|
412
|
-
signer, cred, voucher = agent_call
|
|
424
|
+
signer, cred, voucher, vkey = agent_call
|
|
413
425
|
pub = signer.get_public_key_multikey()
|
|
414
426
|
# Even with a current epoch, critical demands a live co-sign.
|
|
415
427
|
without = verify_agent_call(
|
|
416
428
|
cred,
|
|
417
429
|
public_key=pub,
|
|
418
430
|
session_voucher=voucher,
|
|
431
|
+
voucher_public_key=vkey,
|
|
419
432
|
trust_threshold=0.5,
|
|
420
433
|
consequence=CONSEQUENCE_CRITICAL,
|
|
421
434
|
last_seen_authority_epoch=5,
|
|
@@ -427,6 +440,7 @@ def test_critical_requires_live_cosign(agent_call):
|
|
|
427
440
|
cred,
|
|
428
441
|
public_key=pub,
|
|
429
442
|
session_voucher=voucher,
|
|
443
|
+
voucher_public_key=vkey,
|
|
430
444
|
trust_threshold=0.5,
|
|
431
445
|
consequence=CONSEQUENCE_CRITICAL,
|
|
432
446
|
last_seen_authority_epoch=5,
|
|
@@ -437,13 +451,14 @@ def test_critical_requires_live_cosign(agent_call):
|
|
|
437
451
|
|
|
438
452
|
|
|
439
453
|
def test_voucher_epoch_read_from_credential_subject(agent_call):
|
|
440
|
-
signer, cred, voucher = agent_call
|
|
454
|
+
signer, cred, voucher, vkey = agent_call
|
|
441
455
|
pub = signer.get_public_key_multikey()
|
|
442
456
|
# Do not pass voucher_authority_epoch explicitly; it is read from the voucher.
|
|
443
457
|
v = verify_agent_call(
|
|
444
458
|
cred,
|
|
445
459
|
public_key=pub,
|
|
446
460
|
session_voucher=voucher,
|
|
461
|
+
voucher_public_key=vkey,
|
|
447
462
|
trust_threshold=0.5,
|
|
448
463
|
consequence=CONSEQUENCE_SENSITIVE,
|
|
449
464
|
last_seen_authority_epoch=6,
|
|
@@ -217,10 +217,16 @@ class TestDelegation:
|
|
|
217
217
|
|
|
218
218
|
kp, psigner = principal
|
|
219
219
|
grant = vouch.delegate(
|
|
220
|
-
action="charge",
|
|
220
|
+
action="charge",
|
|
221
|
+
target="api.bank",
|
|
222
|
+
resource="invoices",
|
|
223
|
+
to=env_identity.did,
|
|
224
|
+
signer=psigner,
|
|
221
225
|
)
|
|
222
226
|
|
|
223
|
-
|
|
227
|
+
# The call's intent must sit inside the grant: same action and target,
|
|
228
|
+
# a sub-resource of the granted resource.
|
|
229
|
+
@signed(action="charge", target="api.bank", resource="invoices/42", parent=grant)
|
|
224
230
|
def charge_invoice(invoice_id):
|
|
225
231
|
return current_credential()
|
|
226
232
|
|
|
@@ -229,15 +235,21 @@ class TestDelegation:
|
|
|
229
235
|
assert chain and len(chain) == 1
|
|
230
236
|
assert chain[0]["issuer"] == kp.did
|
|
231
237
|
assert chain[0]["subject"] == env_identity.did
|
|
232
|
-
# The agent's
|
|
233
|
-
|
|
238
|
+
# The agent's credential verifies under its own key, and its chain
|
|
239
|
+
# verifies against the principal's key.
|
|
240
|
+
ok, _ = Verifier.verify(cred, pubkey, trusted_roots={kp.did: kp.public_key_jwk})
|
|
241
|
+
assert ok
|
|
234
242
|
|
|
235
243
|
def test_narrowing_is_enforced_no_widened_credential(self, env_identity, principal):
|
|
236
244
|
kp, psigner = principal
|
|
237
245
|
import vouch
|
|
238
246
|
|
|
239
247
|
grant = vouch.delegate(
|
|
240
|
-
action="charge",
|
|
248
|
+
action="charge",
|
|
249
|
+
target="api.bank",
|
|
250
|
+
resource="invoices",
|
|
251
|
+
to=env_identity.did,
|
|
252
|
+
signer=psigner,
|
|
241
253
|
)
|
|
242
254
|
|
|
243
255
|
# Attempt to act OUTSIDE the granted resource. Signing fails the
|
|
@@ -257,7 +269,11 @@ class TestDelegation:
|
|
|
257
269
|
import vouch
|
|
258
270
|
|
|
259
271
|
grant = vouch.delegate(
|
|
260
|
-
action="charge",
|
|
272
|
+
action="charge",
|
|
273
|
+
target="api.bank",
|
|
274
|
+
resource="invoices",
|
|
275
|
+
to=env_identity.did,
|
|
276
|
+
signer=psigner,
|
|
261
277
|
)
|
|
262
278
|
|
|
263
279
|
@signed(action="charge", target="api.bank", resource="invoices/42", parent=grant)
|
|
@@ -167,3 +167,124 @@ class TestChainAndAccumulation:
|
|
|
167
167
|
def test_empty_chain_rejected(self):
|
|
168
168
|
with pytest.raises(CaveatError):
|
|
169
169
|
verify_capability([], {}.get, {})
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ---------------------------------------------------------------------------
|
|
173
|
+
# Linkage, narrowing, expiry, and revocation of capability links
|
|
174
|
+
# ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class TestLinkSecurity:
|
|
178
|
+
def _parties(self):
|
|
179
|
+
skp, store = _id("store.example.com")
|
|
180
|
+
mkp, mgr = _id("manager.example.com")
|
|
181
|
+
xkp, stranger = _id("stranger.example.com")
|
|
182
|
+
akp, agent = _id("agent.example.com")
|
|
183
|
+
keys = {
|
|
184
|
+
store.get_did(): skp.public_key_jwk,
|
|
185
|
+
mgr.get_did(): mkp.public_key_jwk,
|
|
186
|
+
stranger.get_did(): xkp.public_key_jwk,
|
|
187
|
+
agent.get_did(): akp.public_key_jwk,
|
|
188
|
+
}
|
|
189
|
+
return store, mgr, stranger, agent, keys
|
|
190
|
+
|
|
191
|
+
def _root(self, store, mgr, **kw):
|
|
192
|
+
return build_capability(
|
|
193
|
+
store,
|
|
194
|
+
to=mgr.get_did(),
|
|
195
|
+
attenuation={"action": "refund"},
|
|
196
|
+
caveats=[value_ceiling("cap", limit=200)],
|
|
197
|
+
**kw,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def test_only_the_named_delegatee_can_issue_the_next_link(self):
|
|
201
|
+
"""The probe: a stranger (not the delegatee) issues a widened child."""
|
|
202
|
+
from vouch.caveats import REASON_NOT_DELEGATEE
|
|
203
|
+
|
|
204
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
205
|
+
root = self._root(store, mgr)
|
|
206
|
+
child = build_capability(
|
|
207
|
+
stranger, to=agent.get_did(), attenuation={"action": "*"}, parent=root
|
|
208
|
+
)
|
|
209
|
+
r = verify_capability([root, child], keys.get, {"amount": 100}, root_issuer=store.get_did())
|
|
210
|
+
assert r == REASON_NOT_DELEGATEE
|
|
211
|
+
|
|
212
|
+
def test_child_attenuation_cannot_widen_parent(self):
|
|
213
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
214
|
+
root = self._root(store, mgr)
|
|
215
|
+
child = build_capability(mgr, to=agent.get_did(), attenuation={"action": "*"}, parent=root)
|
|
216
|
+
r = verify_capability([root, child], keys.get, {"amount": 100}, root_issuer=store.get_did())
|
|
217
|
+
assert r == "scope_exceeds_parent:action"
|
|
218
|
+
|
|
219
|
+
def test_links_carry_expiry(self):
|
|
220
|
+
store, mgr, *_ = self._parties()
|
|
221
|
+
root = self._root(store, mgr, valid_seconds=600)
|
|
222
|
+
assert "validFrom" in root and "validUntil" in root
|
|
223
|
+
|
|
224
|
+
def test_expired_link_fails(self):
|
|
225
|
+
from datetime import datetime, timedelta, timezone
|
|
226
|
+
|
|
227
|
+
from vouch.caveats import REASON_EXPIRED
|
|
228
|
+
|
|
229
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
230
|
+
root = self._root(store, mgr, valid_seconds=60)
|
|
231
|
+
later = datetime.now(timezone.utc) + timedelta(hours=2)
|
|
232
|
+
r = verify_capability(
|
|
233
|
+
[root], keys.get, {"amount": 10}, root_issuer=store.get_did(), now=later
|
|
234
|
+
)
|
|
235
|
+
assert r == REASON_EXPIRED
|
|
236
|
+
|
|
237
|
+
def test_link_without_expiry_fails(self):
|
|
238
|
+
from vouch.caveats import REASON_MALFORMED_LINK, _attach_proof
|
|
239
|
+
|
|
240
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
241
|
+
root = self._root(store, mgr)
|
|
242
|
+
undated = {k: v for k, v in root.items() if k not in ("proof", "validUntil")}
|
|
243
|
+
undated = _attach_proof(undated, store)
|
|
244
|
+
r = verify_capability([undated], keys.get, {"amount": 10}, root_issuer=store.get_did())
|
|
245
|
+
assert r == REASON_MALFORMED_LINK
|
|
246
|
+
|
|
247
|
+
def test_child_cannot_outlive_parent(self):
|
|
248
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
249
|
+
root = self._root(store, mgr, valid_seconds=60)
|
|
250
|
+
child = build_capability(
|
|
251
|
+
mgr,
|
|
252
|
+
to=agent.get_did(),
|
|
253
|
+
attenuation={"action": "refund"},
|
|
254
|
+
parent=root,
|
|
255
|
+
valid_seconds=86400,
|
|
256
|
+
)
|
|
257
|
+
# The builder clamps the child to its parent's window ...
|
|
258
|
+
assert child["validUntil"] <= root["validUntil"]
|
|
259
|
+
assert (
|
|
260
|
+
verify_capability([root, child], keys.get, {"amount": 10}, root_issuer=store.get_did())
|
|
261
|
+
is None
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def test_revoked_link_or_issuer_fails(self):
|
|
265
|
+
from vouch.caveats import REASON_REVOKED
|
|
266
|
+
|
|
267
|
+
store, mgr, stranger, agent, keys = self._parties()
|
|
268
|
+
root = self._root(store, mgr)
|
|
269
|
+
child = build_capability(
|
|
270
|
+
mgr, to=agent.get_did(), attenuation={"action": "refund"}, parent=root
|
|
271
|
+
)
|
|
272
|
+
chain = [root, child]
|
|
273
|
+
ctx = {"amount": 10}
|
|
274
|
+
assert verify_capability(chain, keys.get, ctx, root_issuer=store.get_did()) is None
|
|
275
|
+
assert (
|
|
276
|
+
verify_capability(
|
|
277
|
+
chain, keys.get, ctx, root_issuer=store.get_did(), revoked={root["id"]}
|
|
278
|
+
)
|
|
279
|
+
== REASON_REVOKED
|
|
280
|
+
)
|
|
281
|
+
assert (
|
|
282
|
+
verify_capability(
|
|
283
|
+
chain,
|
|
284
|
+
keys.get,
|
|
285
|
+
ctx,
|
|
286
|
+
root_issuer=store.get_did(),
|
|
287
|
+
revoked=lambda i: i == mgr.get_did(),
|
|
288
|
+
)
|
|
289
|
+
== REASON_REVOKED
|
|
290
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""The CCG demo scripts run offline and every case matches its expected outcome."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
13
|
+
DEMO = ROOT / "examples" / "ccg_demo"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.parametrize("script", ["01_delegation.py", "02_trust_decay.py", "03_heartbeat.py"])
|
|
17
|
+
def test_demo_script_passes(script):
|
|
18
|
+
env = dict(os.environ, PYTHONPATH=str(ROOT), PYTHONDONTWRITEBYTECODE="1")
|
|
19
|
+
proc = subprocess.run(
|
|
20
|
+
[sys.executable, str(DEMO / script)],
|
|
21
|
+
cwd=str(ROOT),
|
|
22
|
+
env=env,
|
|
23
|
+
capture_output=True,
|
|
24
|
+
text=True,
|
|
25
|
+
timeout=60,
|
|
26
|
+
)
|
|
27
|
+
assert proc.returncode == 0, proc.stdout + proc.stderr
|
|
28
|
+
assert "UNEXPECTED" not in proc.stdout
|
|
29
|
+
assert "matched the expected outcome" in proc.stdout
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Python against the shared delegation-chain vectors (the interop contract)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import json
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from vouch.verifier import Verifier, verify_delegation_chain
|
|
13
|
+
|
|
14
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
15
|
+
VECTOR = ROOT / "test-vectors" / "delegation-chain" / "vector.json"
|
|
16
|
+
CASES = json.loads(VECTOR.read_text(encoding="utf-8"))["cases"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _at(iso: str) -> datetime:
|
|
20
|
+
return datetime.fromisoformat(iso.replace("Z", "+00:00"))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_vector_file_matches_generator():
|
|
24
|
+
spec = importlib.util.spec_from_file_location(
|
|
25
|
+
"gen_delegation_chain_vectors", ROOT / "scripts" / "gen_delegation_chain_vectors.py"
|
|
26
|
+
)
|
|
27
|
+
gen = importlib.util.module_from_spec(spec)
|
|
28
|
+
spec.loader.exec_module(gen)
|
|
29
|
+
assert gen.render() == VECTOR.read_text(encoding="utf-8")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize("case", CASES, ids=[c["name"] for c in CASES])
|
|
33
|
+
def test_chain_verdict(case):
|
|
34
|
+
assert verify_delegation_chain(case["credential"], now=_at(case["now"])) == case["expected"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.parametrize("case", CASES, ids=[c["name"] for c in CASES])
|
|
38
|
+
def test_full_verify_agrees(case):
|
|
39
|
+
cred = case["credential"]
|
|
40
|
+
issuer = cred["issuer"]
|
|
41
|
+
leaf_key = issuer.split("did:key:", 1)[1]
|
|
42
|
+
ok, _ = Verifier.verify(cred, public_key=leaf_key, now=_at(case["now"]))
|
|
43
|
+
assert ok is (case["expected"] is None)
|