a2aclear 1.0.0__py3-none-any.whl
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.
- a2aclear/__init__.py +41 -0
- a2aclear/clearinghouse.py +65 -0
- a2aclear/cli.py +112 -0
- a2aclear/contract.py +85 -0
- a2aclear/escrow.py +171 -0
- a2aclear/fiat_bridge.py +64 -0
- a2aclear/identity.py +112 -0
- a2aclear/integrations/__init__.py +8 -0
- a2aclear/integrations/crewai.py +65 -0
- a2aclear/integrations/langchain.py +87 -0
- a2aclear/negotiator.py +111 -0
- a2aclear/reputation.py +95 -0
- a2aclear/security.py +182 -0
- a2aclear-1.0.0.dist-info/METADATA +189 -0
- a2aclear-1.0.0.dist-info/RECORD +18 -0
- a2aclear-1.0.0.dist-info/WHEEL +5 -0
- a2aclear-1.0.0.dist-info/entry_points.txt +2 -0
- a2aclear-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
a2aclear.integrations.crewai
|
|
3
|
+
CrewAI Multi-Agent Swarm Financial Settlement Adapter
|
|
4
|
+
Enables autonomous financial settlements between members of a CrewAI swarm.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, Any, List, Optional
|
|
9
|
+
from ..identity import AgentIdentity
|
|
10
|
+
from ..contract import TaskContract
|
|
11
|
+
from ..escrow import MicroEscrowVault
|
|
12
|
+
|
|
13
|
+
class A2ACrewSettlementHandler:
|
|
14
|
+
"""
|
|
15
|
+
Financial manager for CrewAI swarms.
|
|
16
|
+
Assigns Ed25519 identities to each crew agent and manages inter-agent micropayments.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, crew_name: str = "UnnatEnterpriseSwarm"):
|
|
20
|
+
self.crew_name = crew_name
|
|
21
|
+
self.vault = MicroEscrowVault()
|
|
22
|
+
self.agent_identities: Dict[str, AgentIdentity] = {}
|
|
23
|
+
|
|
24
|
+
def register_crew_agent(self, agent_role: str, initial_deposit_paise: int = 10000) -> AgentIdentity:
|
|
25
|
+
"""Assign an Ed25519 identity to a crew agent and fund its balance."""
|
|
26
|
+
identity = AgentIdentity.generate()
|
|
27
|
+
self.agent_identities[agent_role] = identity
|
|
28
|
+
if initial_deposit_paise > 0:
|
|
29
|
+
self.vault.deposit(identity.did, initial_deposit_paise)
|
|
30
|
+
return identity
|
|
31
|
+
|
|
32
|
+
def settle_crew_step(
|
|
33
|
+
self,
|
|
34
|
+
from_role: str,
|
|
35
|
+
to_role: str,
|
|
36
|
+
task_name: str,
|
|
37
|
+
step_cost_paise: int,
|
|
38
|
+
task_output: Any
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""
|
|
41
|
+
Executes an atomic micro-settlement when one crew agent hands off work to another.
|
|
42
|
+
"""
|
|
43
|
+
buyer = self.agent_identities.get(from_role) or self.register_crew_agent(from_role)
|
|
44
|
+
seller = self.agent_identities.get(to_role) or self.register_crew_agent(to_role, initial_deposit_paise=0)
|
|
45
|
+
|
|
46
|
+
contract = TaskContract(
|
|
47
|
+
task_id=f"crew_{from_role}_{to_role}_{task_name}",
|
|
48
|
+
originator_did=buyer.did,
|
|
49
|
+
provider_did=seller.did,
|
|
50
|
+
amount_paise=step_cost_paise,
|
|
51
|
+
currency="INR"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
self.vault.lock_escrow(contract)
|
|
55
|
+
receipt = self.vault.verify_and_settle(contract, task_output)
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"success": True,
|
|
59
|
+
"task": task_name,
|
|
60
|
+
"from": from_role,
|
|
61
|
+
"to": to_role,
|
|
62
|
+
"settled_hash": receipt["settlement_hash"],
|
|
63
|
+
"worker_received_paise": receipt["provider_received_paise"],
|
|
64
|
+
"protocol_fee_paise": receipt["protocol_fee_paise"]
|
|
65
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
a2aclear.integrations.langchain
|
|
3
|
+
LangChain & LangGraph Integration Adapter for A2AClear Protocol
|
|
4
|
+
Allows autonomous LangChain agents to contract, lock escrow, and settle with peer agents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
from ..identity import AgentIdentity
|
|
10
|
+
from ..negotiator import AutonomousNegotiator
|
|
11
|
+
from ..escrow import MicroEscrowVault
|
|
12
|
+
|
|
13
|
+
class A2AClearLangChainTool:
|
|
14
|
+
"""
|
|
15
|
+
LangChain compatible tool for autonomous financial delegation and micro-escrow settlement.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
name: str = "a2aclear_settlement"
|
|
19
|
+
description: str = (
|
|
20
|
+
"Use this tool to autonomously hire, lock micro-escrow, and pay another AI agent "
|
|
21
|
+
"for a specific subtask using the A2AClear sovereign clearinghouse protocol."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def __init__(self, identity: Optional[AgentIdentity] = None, vault: Optional[MicroEscrowVault] = None):
|
|
25
|
+
self.identity = identity or AgentIdentity.generate()
|
|
26
|
+
self.vault = vault or MicroEscrowVault()
|
|
27
|
+
self.negotiator = AutonomousNegotiator(self.identity)
|
|
28
|
+
|
|
29
|
+
def run(self, action: str, **kwargs) -> str:
|
|
30
|
+
"""Execute settlement actions from a LangChain agent turn."""
|
|
31
|
+
action = action.upper()
|
|
32
|
+
|
|
33
|
+
if action == "GET_BALANCE":
|
|
34
|
+
balance = self.vault.get_balance(self.identity.did)
|
|
35
|
+
return json.dumps({
|
|
36
|
+
"agent_did": self.identity.did,
|
|
37
|
+
"balance_inr": f"₹{balance / 100:.2f}",
|
|
38
|
+
"balance_paise": balance
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
elif action == "HIRE_AND_LOCK_ESCROW":
|
|
42
|
+
task_id = kwargs.get("task_id", f"lc_task_{int(self.identity.did[-8:], 16)}")
|
|
43
|
+
provider_did = kwargs.get("provider_did")
|
|
44
|
+
amount_paise = int(kwargs.get("amount_paise", 5000))
|
|
45
|
+
task_spec = kwargs.get("task_spec", {})
|
|
46
|
+
|
|
47
|
+
if not provider_did:
|
|
48
|
+
return json.dumps({"error": "provider_did is required to delegate task"})
|
|
49
|
+
|
|
50
|
+
proposal = self.negotiator.propose_contract(
|
|
51
|
+
task_id=task_id,
|
|
52
|
+
provider_did=provider_did,
|
|
53
|
+
initial_bid_paise=amount_paise,
|
|
54
|
+
currency="INR",
|
|
55
|
+
task_spec=task_spec
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
contract = self.negotiator.active_contracts[task_id]
|
|
59
|
+
try:
|
|
60
|
+
escrow_record = self.vault.lock_escrow(contract)
|
|
61
|
+
return json.dumps({
|
|
62
|
+
"status": "ESCROW_LOCKED",
|
|
63
|
+
"escrow_id": escrow_record["escrow_id"],
|
|
64
|
+
"task_id": task_id,
|
|
65
|
+
"locked_amount_inr": f"₹{amount_paise / 100:.2f}",
|
|
66
|
+
"protocol_fee_inr": f"₹{contract.protocol_fee_paise / 100:.2f} (0.50%)"
|
|
67
|
+
})
|
|
68
|
+
except Exception as e:
|
|
69
|
+
return json.dumps({"error": f"Failed to lock escrow: {str(e)}"})
|
|
70
|
+
|
|
71
|
+
elif action == "SETTLE_DELIVERABLE":
|
|
72
|
+
task_id = kwargs.get("task_id")
|
|
73
|
+
deliverable = kwargs.get("deliverable", {})
|
|
74
|
+
contract = self.negotiator.active_contracts.get(task_id)
|
|
75
|
+
if not contract:
|
|
76
|
+
return json.dumps({"error": f"Contract {task_id} not found"})
|
|
77
|
+
|
|
78
|
+
receipt = self.vault.verify_and_settle(contract, deliverable)
|
|
79
|
+
return json.dumps({
|
|
80
|
+
"status": "SETTLED",
|
|
81
|
+
"settlement_hash": receipt["settlement_hash"],
|
|
82
|
+
"provider_payout_inr": f"₹{receipt['provider_received_paise'] / 100:.2f}",
|
|
83
|
+
"protocol_fee_inr": f"₹{receipt['protocol_fee_paise'] / 100:.2f}"
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
return json.dumps({"error": f"Unknown A2AClear action: {action}"})
|
a2aclear/negotiator.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
a2aclear.negotiator
|
|
3
|
+
Autonomous Agent Negotiation Protocol (ANP) Finite-State Machine
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import time
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, Any, Optional, Tuple
|
|
9
|
+
from .identity import AgentIdentity
|
|
10
|
+
from .contract import TaskContract, ContractState
|
|
11
|
+
|
|
12
|
+
class AutonomousNegotiator:
|
|
13
|
+
"""
|
|
14
|
+
Autonomous Negotiation Protocol (ANP) Engine.
|
|
15
|
+
Handles bilateral SLA negotiation, automated counter-offers, and Ed25519 countersigning.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, identity: AgentIdentity, max_budget_paise: int = 1000000):
|
|
19
|
+
self.identity = identity
|
|
20
|
+
self.max_budget_paise = max_budget_paise
|
|
21
|
+
self.active_contracts: Dict[str, TaskContract] = {}
|
|
22
|
+
|
|
23
|
+
def propose_contract(
|
|
24
|
+
self,
|
|
25
|
+
task_id: str,
|
|
26
|
+
provider_did: str,
|
|
27
|
+
initial_bid_paise: int,
|
|
28
|
+
currency: str = "INR",
|
|
29
|
+
sla_timeout_seconds: int = 300,
|
|
30
|
+
task_spec: Dict[str, Any] = None
|
|
31
|
+
) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Agent Originator proposes a task contract to a provider agent.
|
|
34
|
+
"""
|
|
35
|
+
spec_str = json.dumps(task_spec or {}, sort_keys=True)
|
|
36
|
+
import hashlib
|
|
37
|
+
spec_hash = hashlib.sha256(spec_str.encode("utf-8")).hexdigest()
|
|
38
|
+
|
|
39
|
+
contract = TaskContract(
|
|
40
|
+
task_id=task_id,
|
|
41
|
+
originator_did=self.identity.did,
|
|
42
|
+
provider_did=provider_did,
|
|
43
|
+
amount_paise=initial_bid_paise,
|
|
44
|
+
currency=currency,
|
|
45
|
+
sla_timeout_seconds=sla_timeout_seconds,
|
|
46
|
+
task_spec_hash=spec_hash
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
contract_hash = contract.compute_contract_hash()
|
|
50
|
+
contract.originator_signature = self.identity.sign(contract_hash.encode("utf-8")).hex()
|
|
51
|
+
contract.state = ContractState.PROPOSED
|
|
52
|
+
self.active_contracts[task_id] = contract
|
|
53
|
+
|
|
54
|
+
return self.identity.sign_envelope(
|
|
55
|
+
payload=contract.to_dict(),
|
|
56
|
+
action="PROPOSE_CONTRACT",
|
|
57
|
+
recipient_did=provider_did
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def evaluate_proposal(
|
|
61
|
+
self,
|
|
62
|
+
envelope: Dict[str, Any],
|
|
63
|
+
minimum_acceptable_paise: int
|
|
64
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
65
|
+
"""
|
|
66
|
+
Agent Provider evaluates an incoming proposal envelope.
|
|
67
|
+
Returns ("ACCEPT" | "COUNTER" | "REJECT", response_envelope).
|
|
68
|
+
"""
|
|
69
|
+
if not AgentIdentity.verify_envelope(envelope):
|
|
70
|
+
raise ValueError("Cryptographic envelope signature verification failed.")
|
|
71
|
+
|
|
72
|
+
payload = envelope["payload"]
|
|
73
|
+
task_id = payload["task_id"]
|
|
74
|
+
offered_amount = payload["amount_paise"]
|
|
75
|
+
originator_did = envelope["sender_did"]
|
|
76
|
+
|
|
77
|
+
if offered_amount >= minimum_acceptable_paise:
|
|
78
|
+
# Accept and countersign
|
|
79
|
+
contract = TaskContract(
|
|
80
|
+
task_id=task_id,
|
|
81
|
+
originator_did=originator_did,
|
|
82
|
+
provider_did=self.identity.did,
|
|
83
|
+
amount_paise=offered_amount,
|
|
84
|
+
currency=payload.get("currency", "INR"),
|
|
85
|
+
sla_timeout_seconds=payload.get("sla_timeout_seconds", 300),
|
|
86
|
+
task_spec_hash=payload.get("task_spec_hash", "")
|
|
87
|
+
)
|
|
88
|
+
contract_hash = contract.compute_contract_hash()
|
|
89
|
+
contract.originator_signature = payload.get("originator_signature")
|
|
90
|
+
contract.provider_signature = self.identity.sign(contract_hash.encode("utf-8")).hex()
|
|
91
|
+
contract.state = ContractState.AGREED
|
|
92
|
+
self.active_contracts[task_id] = contract
|
|
93
|
+
|
|
94
|
+
resp_env = self.identity.sign_envelope(
|
|
95
|
+
payload=contract.to_dict(),
|
|
96
|
+
action="ACCEPT_CONTRACT",
|
|
97
|
+
recipient_did=originator_did
|
|
98
|
+
)
|
|
99
|
+
return "ACCEPT", resp_env
|
|
100
|
+
else:
|
|
101
|
+
# Counter-offer with minimum price
|
|
102
|
+
counter_payload = dict(payload)
|
|
103
|
+
counter_payload["amount_paise"] = minimum_acceptable_paise
|
|
104
|
+
counter_payload["counter_reason"] = "Requested price below compute/inference margin threshold."
|
|
105
|
+
|
|
106
|
+
resp_env = self.identity.sign_envelope(
|
|
107
|
+
payload=counter_payload,
|
|
108
|
+
action="COUNTER_BID",
|
|
109
|
+
recipient_did=originator_did
|
|
110
|
+
)
|
|
111
|
+
return "COUNTER", resp_env
|
a2aclear/reputation.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
a2aclear.reputation
|
|
3
|
+
Sovereign Reputation Ledger & Collateral Staking Defense System
|
|
4
|
+
Maintains mathematical agent reputation, trust scores, and anti-sybil collateral slashing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import math
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
class AgentReputationLedger:
|
|
12
|
+
"""
|
|
13
|
+
Sovereign Decentralized Reputation & Collateral Staking Engine.
|
|
14
|
+
Computes deterministic reputation metrics based on historical contract execution proofs.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
# did -> record
|
|
19
|
+
self.records: Dict[str, Dict[str, Any]] = {}
|
|
20
|
+
|
|
21
|
+
def get_or_create(self, did: str) -> Dict[str, Any]:
|
|
22
|
+
if did not in self.records:
|
|
23
|
+
self.records[did] = {
|
|
24
|
+
"did": did,
|
|
25
|
+
"created_at": int(time.time()),
|
|
26
|
+
"total_tasks_attempted": 0,
|
|
27
|
+
"total_tasks_completed": 0,
|
|
28
|
+
"total_volume_settled_paise": 0,
|
|
29
|
+
"sla_breaches": 0,
|
|
30
|
+
"slashed_count": 0,
|
|
31
|
+
"collateral_staked_paise": 0,
|
|
32
|
+
"is_blacklisted": False,
|
|
33
|
+
"reputation_score": 100.0 # Base score for authenticated Ed25519 node
|
|
34
|
+
}
|
|
35
|
+
return self.records[did]
|
|
36
|
+
|
|
37
|
+
def stake_collateral(self, did: str, amount_paise: int):
|
|
38
|
+
"""Stake bonding collateral to unlock high-tier B2B agent contracts."""
|
|
39
|
+
record = self.get_or_create(did)
|
|
40
|
+
if record["is_blacklisted"]:
|
|
41
|
+
raise PermissionError("Blacklisted agent node cannot stake collateral.")
|
|
42
|
+
record["collateral_staked_paise"] += amount_paise
|
|
43
|
+
self.recompute_score(did)
|
|
44
|
+
|
|
45
|
+
def record_settlement(self, did: str, volume_paise: int, latency_ms: float, sla_target_ms: float):
|
|
46
|
+
"""Record a successful atomic settlement proof."""
|
|
47
|
+
record = self.get_or_create(did)
|
|
48
|
+
if record["is_blacklisted"]:
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
record["total_tasks_attempted"] += 1
|
|
52
|
+
record["total_tasks_completed"] += 1
|
|
53
|
+
record["total_volume_settled_paise"] += volume_paise
|
|
54
|
+
|
|
55
|
+
if latency_ms > sla_target_ms:
|
|
56
|
+
record["sla_breaches"] += 1
|
|
57
|
+
|
|
58
|
+
self.recompute_score(did)
|
|
59
|
+
|
|
60
|
+
def record_slash(self, did: str, reason: str) -> int:
|
|
61
|
+
"""Slash 100% of collateral upon malicious behavior or cryptographic forgery."""
|
|
62
|
+
record = self.get_or_create(did)
|
|
63
|
+
record["slashed_count"] += 1
|
|
64
|
+
record["is_blacklisted"] = True
|
|
65
|
+
record["reputation_score"] = 0.0
|
|
66
|
+
slashed_amount = record["collateral_staked_paise"]
|
|
67
|
+
record["collateral_staked_paise"] = 0
|
|
68
|
+
record["slash_reason"] = reason
|
|
69
|
+
return slashed_amount
|
|
70
|
+
|
|
71
|
+
def recompute_score(self, did: str) -> float:
|
|
72
|
+
"""
|
|
73
|
+
Mathematical Reputation Formula:
|
|
74
|
+
R = 0.50 * CompletionRatio + 0.30 * SLARatio + 0.20 * VolumeLog - SlashedPenalty
|
|
75
|
+
"""
|
|
76
|
+
record = self.records[did]
|
|
77
|
+
if record["is_blacklisted"]:
|
|
78
|
+
return 0.0
|
|
79
|
+
|
|
80
|
+
total = record["total_tasks_attempted"]
|
|
81
|
+
if total == 0:
|
|
82
|
+
return 100.0
|
|
83
|
+
|
|
84
|
+
completed = record["total_tasks_completed"]
|
|
85
|
+
breaches = record["sla_breaches"]
|
|
86
|
+
|
|
87
|
+
completion_ratio = (completed / total) * 100.0
|
|
88
|
+
sla_ratio = max(0.0, ((total - breaches) / total) * 100.0)
|
|
89
|
+
|
|
90
|
+
volume_inr = record["total_volume_settled_paise"] / 100.0
|
|
91
|
+
volume_boost = min(100.0, math.log10(max(1.0, volume_inr)) * 20.0)
|
|
92
|
+
|
|
93
|
+
score = (0.50 * completion_ratio) + (0.35 * sla_ratio) + (0.15 * volume_boost)
|
|
94
|
+
record["reputation_score"] = round(min(100.0, max(0.0, score)), 2)
|
|
95
|
+
return record["reputation_score"]
|
a2aclear/security.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
a2aclear.security
|
|
3
|
+
Institutional Cybersecurity, Anti-Hacking & Cryptographic IP Armor
|
|
4
|
+
Includes AES-256-GCM Encrypted Keystores, Replay Attack Defense,
|
|
5
|
+
Payload Sanitization, and Sovereign Cryptographic Watermarking.
|
|
6
|
+
|
|
7
|
+
(C) 2026 Unnat AI Solutions. Chief Systems Architect: Rohit.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import time
|
|
12
|
+
import json
|
|
13
|
+
import hashlib
|
|
14
|
+
from typing import Dict, Any, Optional, Set
|
|
15
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
16
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
17
|
+
from cryptography.hazmat.primitives import hashes
|
|
18
|
+
|
|
19
|
+
SOVEREIGN_WATERMARK_SALT = b"ROHIT_UNNAT_AI_SOLUTIONS_SOVEREIGN_ROOT_2026_KURUKSHETRA"
|
|
20
|
+
|
|
21
|
+
class ReplayAttackException(Exception):
|
|
22
|
+
"""Raised when an envelope fails replay attack or timestamp validation."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class MaliciousPayloadException(Exception):
|
|
26
|
+
"""Raised when an incoming deliverable fails security sanitization."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
class EncryptedKeyStore:
|
|
30
|
+
"""
|
|
31
|
+
Military-Grade AES-256-GCM Encrypted Keystore.
|
|
32
|
+
Protects Agent Ed25519 private keys on disk using PBKDF2 key derivation (100,000 rounds).
|
|
33
|
+
Prevents plaintext key theft, memory dumps, and environment variable snooping.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def derive_encryption_key(passphrase: str, salt: bytes) -> bytes:
|
|
38
|
+
kdf = PBKDF2HMAC(
|
|
39
|
+
algorithm=hashes.SHA256(),
|
|
40
|
+
length=32,
|
|
41
|
+
salt=salt,
|
|
42
|
+
iterations=100000,
|
|
43
|
+
)
|
|
44
|
+
return kdf.derive(passphrase.encode("utf-8"))
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def save_encrypted_key(cls, private_hex: str, passphrase: str, filepath: str):
|
|
48
|
+
"""Encrypts and writes private key to secure file."""
|
|
49
|
+
salt = os.urandom(16)
|
|
50
|
+
nonce = os.urandom(12)
|
|
51
|
+
key = cls.derive_encryption_key(passphrase, salt)
|
|
52
|
+
aesgcm = AESGCM(key)
|
|
53
|
+
|
|
54
|
+
ciphertext = aesgcm.encrypt(nonce, private_hex.encode("utf-8"), None)
|
|
55
|
+
|
|
56
|
+
keystore_blob = {
|
|
57
|
+
"version": "A2A_KEYSTORE_V1_AES256GCM",
|
|
58
|
+
"salt_hex": salt.hex(),
|
|
59
|
+
"nonce_hex": nonce.hex(),
|
|
60
|
+
"ciphertext_hex": ciphertext.hex(),
|
|
61
|
+
"created_at": int(time.time()),
|
|
62
|
+
"sovereign_armor": "UNNAT_ENCRYPTED_VAULT"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
66
|
+
json.dump(keystore_blob, f, indent=2)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def load_encrypted_key(cls, passphrase: str, filepath: str) -> str:
|
|
70
|
+
"""Decrypts private key in memory only. Never leaks to disk."""
|
|
71
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
72
|
+
blob = json.load(f)
|
|
73
|
+
|
|
74
|
+
salt = bytes.fromhex(blob["salt_hex"])
|
|
75
|
+
nonce = bytes.fromhex(blob["nonce_hex"])
|
|
76
|
+
ciphertext = bytes.fromhex(blob["ciphertext_hex"])
|
|
77
|
+
|
|
78
|
+
key = cls.derive_encryption_key(passphrase, salt)
|
|
79
|
+
aesgcm = AESGCM(key)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
decrypted_bytes = aesgcm.decrypt(nonce, ciphertext, None)
|
|
83
|
+
return decrypted_bytes.decode("utf-8")
|
|
84
|
+
except Exception:
|
|
85
|
+
raise PermissionError("Decryption failed. Invalid passphrase or corrupted keystore.")
|
|
86
|
+
|
|
87
|
+
class ReplayDefenseValidator:
|
|
88
|
+
"""
|
|
89
|
+
Zero-Trust Replay Attack Defense & Nonce Cache.
|
|
90
|
+
Rejects any transaction envelope outside a 60-second sliding clock window
|
|
91
|
+
or containing an already-used nonce.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(self, max_clock_skew_seconds: int = 60, max_cache_size: int = 100000):
|
|
95
|
+
self.max_clock_skew_seconds = max_clock_skew_seconds
|
|
96
|
+
self.max_cache_size = max_cache_size
|
|
97
|
+
self.seen_nonces: Set[str] = set()
|
|
98
|
+
|
|
99
|
+
def validate_envelope(self, envelope: Dict[str, Any]):
|
|
100
|
+
sender_did = envelope.get("sender_did", "")
|
|
101
|
+
timestamp = envelope.get("timestamp", 0)
|
|
102
|
+
nonce = envelope.get("nonce", None)
|
|
103
|
+
|
|
104
|
+
if nonce is None:
|
|
105
|
+
raise ReplayAttackException("Security violation: Envelope missing cryptographic nonce.")
|
|
106
|
+
|
|
107
|
+
now = int(time.time())
|
|
108
|
+
if abs(now - timestamp) > self.max_clock_skew_seconds:
|
|
109
|
+
raise ReplayAttackException(
|
|
110
|
+
f"Clock skew rejected. Envelope timestamp {timestamp} is outside allowable {self.max_clock_skew_seconds}s window (Current: {now})."
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
nonce_key = f"{sender_did}:{nonce}"
|
|
114
|
+
if nonce_key in self.seen_nonces:
|
|
115
|
+
raise ReplayAttackException(f"REPLAY ATTACK BLOCKED: Nonce {nonce} from {sender_did} already utilized!")
|
|
116
|
+
|
|
117
|
+
# Add to seen nonces
|
|
118
|
+
if len(self.seen_nonces) >= self.max_cache_size:
|
|
119
|
+
self.seen_nonces.clear() # Reset cache on full capacity
|
|
120
|
+
|
|
121
|
+
self.seen_nonces.add(nonce_key)
|
|
122
|
+
|
|
123
|
+
class PayloadSanitizer:
|
|
124
|
+
"""
|
|
125
|
+
Deep Content Inspection & Anti-Injection Guard for AI Agent Deliverables.
|
|
126
|
+
Prevents prompt injection, malicious shell escapes, oversized payloads, and recursive JSON bombs.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
MAX_PAYLOAD_BYTES = 5 * 1024 * 1024 # 5 MB limit
|
|
130
|
+
MAX_DEPTH = 8
|
|
131
|
+
|
|
132
|
+
DANGEROUS_PATTERNS = [
|
|
133
|
+
"__import__", "os.system", "subprocess", "eval(", "exec(",
|
|
134
|
+
"/etc/passwd", "DROP TABLE", "<script>", "javascript:"
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def sanitize(cls, deliverable: Any):
|
|
139
|
+
# 1. Size check
|
|
140
|
+
raw_str = json.dumps(deliverable) if not isinstance(deliverable, str) else deliverable
|
|
141
|
+
if len(raw_str.encode("utf-8")) > cls.MAX_PAYLOAD_BYTES:
|
|
142
|
+
raise MaliciousPayloadException("Payload exceeds 5MB micro-task limit.")
|
|
143
|
+
|
|
144
|
+
# 2. Check depth
|
|
145
|
+
if isinstance(deliverable, (dict, list)):
|
|
146
|
+
cls._check_depth(deliverable, 0)
|
|
147
|
+
|
|
148
|
+
# 3. Check dangerous injection patterns
|
|
149
|
+
for pattern in cls.DANGEROUS_PATTERNS:
|
|
150
|
+
if pattern in raw_str:
|
|
151
|
+
raise MaliciousPayloadException(f"Security Alert: Malicious code injection signature detected ('{pattern}').")
|
|
152
|
+
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def _check_depth(cls, obj: Any, depth: int):
|
|
157
|
+
if depth > cls.MAX_DEPTH:
|
|
158
|
+
raise MaliciousPayloadException("JSON recursion depth bomb detected.")
|
|
159
|
+
if isinstance(obj, dict):
|
|
160
|
+
for v in obj.values():
|
|
161
|
+
cls._check_depth(v, depth + 1)
|
|
162
|
+
elif isinstance(obj, list):
|
|
163
|
+
for item in obj:
|
|
164
|
+
cls._check_depth(item, depth + 1)
|
|
165
|
+
|
|
166
|
+
class SovereignCryptographicWatermark:
|
|
167
|
+
"""
|
|
168
|
+
Steganographic Legal Watermark Engine.
|
|
169
|
+
Embeds an immutable mathematical proof of authorship into contract hashes.
|
|
170
|
+
Can be used in court or intellectual property disputes to prove theft.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def generate_watermark(contract_hash: str) -> str:
|
|
175
|
+
"""Derives a cryptographic authorship attestation tied to Rohit & Unnat AI."""
|
|
176
|
+
data = f"{contract_hash}:{SOVEREIGN_WATERMARK_SALT.decode()}"
|
|
177
|
+
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def verify_authorship(contract_hash: str, claimed_watermark: str) -> bool:
|
|
181
|
+
expected = SovereignCryptographicWatermark.generate_watermark(contract_hash)
|
|
182
|
+
return expected == claimed_watermark
|