verigate-verify 0.1.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.
- verigate_verify-0.1.0/LICENSE +21 -0
- verigate_verify-0.1.0/PKG-INFO +60 -0
- verigate_verify-0.1.0/README.md +38 -0
- verigate_verify-0.1.0/pyproject.toml +34 -0
- verigate_verify-0.1.0/setup.cfg +4 -0
- verigate_verify-0.1.0/src/verigate_verify/__init__.py +2 -0
- verigate_verify-0.1.0/src/verigate_verify/__main__.py +3 -0
- verigate_verify-0.1.0/src/verigate_verify/_verifier.py +245 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/PKG-INFO +60 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/SOURCES.txt +12 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/dependency_links.txt +1 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/entry_points.txt +2 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/requires.txt +1 -0
- verigate_verify-0.1.0/src/verigate_verify.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rudimentall1
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: verigate-verify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Offline verifier for Verigate authority proofs: check a portable proof with only the issuer's public key.
|
|
5
|
+
Author: rudimentall1
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rudimentall1/verigate
|
|
8
|
+
Project-URL: Source, https://github.com/rudimentall1/verigate/tree/main/packaging/verigate-verify
|
|
9
|
+
Project-URL: Issues, https://github.com/rudimentall1/verigate/issues
|
|
10
|
+
Project-URL: Proof format, https://github.com/rudimentall1/verigate/blob/main/docs/verigate-authority-proof-v1.md
|
|
11
|
+
Project-URL: Threat model, https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md
|
|
12
|
+
Keywords: ai-agents,authorization,verification,ed25519,audit,x402
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: cryptography<51,>=48.0.1
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# verigate-verify
|
|
24
|
+
|
|
25
|
+
Offline verifier for [Verigate](https://github.com/rudimentall1/verigate) authority proofs.
|
|
26
|
+
|
|
27
|
+
Verigate decides, per action, whether an AI agent had the authority to act, issues a one-time signed
|
|
28
|
+
authorization, and exports a portable proof. This package is the standalone verifier for that proof:
|
|
29
|
+
**you need only the proof file and the issuer's Ed25519 public key.** No Verigate server, database or API.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install verigate-verify
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Use
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
verigate-verify proof.json --public-key issuer.pub
|
|
41
|
+
verigate-verify proof.json --public-key issuer.pub --format json
|
|
42
|
+
# or
|
|
43
|
+
python -m verigate_verify proof.json --public-key issuer.pub
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The result is `VALID` or `INVALID` with a reason (for example `proof package digest mismatch` if the
|
|
47
|
+
evidence was altered). Exit code is 0 for a valid proof and non-zero otherwise.
|
|
48
|
+
|
|
49
|
+
A sample proof and issuer key are in the repository under `examples/authority-proof/`.
|
|
50
|
+
|
|
51
|
+
## What it does and does not tell you
|
|
52
|
+
|
|
53
|
+
It checks the package digest, the evidence manifest, the issuer signature against the public key you trust,
|
|
54
|
+
the evidence graph and the authority protocol assertions. It does not decide whether the issuer's key is the
|
|
55
|
+
right one to trust (you must obtain the public key through a channel you trust), and it does not prove that no
|
|
56
|
+
other actions were taken. See the [threat model](https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md).
|
|
57
|
+
|
|
58
|
+
## Source
|
|
59
|
+
|
|
60
|
+
This is the same code as `tools/verigate_proof_verifier.py` in the main repository. MIT licensed.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# verigate-verify
|
|
2
|
+
|
|
3
|
+
Offline verifier for [Verigate](https://github.com/rudimentall1/verigate) authority proofs.
|
|
4
|
+
|
|
5
|
+
Verigate decides, per action, whether an AI agent had the authority to act, issues a one-time signed
|
|
6
|
+
authorization, and exports a portable proof. This package is the standalone verifier for that proof:
|
|
7
|
+
**you need only the proof file and the issuer's Ed25519 public key.** No Verigate server, database or API.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install verigate-verify
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Use
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
verigate-verify proof.json --public-key issuer.pub
|
|
19
|
+
verigate-verify proof.json --public-key issuer.pub --format json
|
|
20
|
+
# or
|
|
21
|
+
python -m verigate_verify proof.json --public-key issuer.pub
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The result is `VALID` or `INVALID` with a reason (for example `proof package digest mismatch` if the
|
|
25
|
+
evidence was altered). Exit code is 0 for a valid proof and non-zero otherwise.
|
|
26
|
+
|
|
27
|
+
A sample proof and issuer key are in the repository under `examples/authority-proof/`.
|
|
28
|
+
|
|
29
|
+
## What it does and does not tell you
|
|
30
|
+
|
|
31
|
+
It checks the package digest, the evidence manifest, the issuer signature against the public key you trust,
|
|
32
|
+
the evidence graph and the authority protocol assertions. It does not decide whether the issuer's key is the
|
|
33
|
+
right one to trust (you must obtain the public key through a channel you trust), and it does not prove that no
|
|
34
|
+
other actions were taken. See the [threat model](https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md).
|
|
35
|
+
|
|
36
|
+
## Source
|
|
37
|
+
|
|
38
|
+
This is the same code as `tools/verigate_proof_verifier.py` in the main repository. MIT licensed.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "verigate-verify"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Offline verifier for Verigate authority proofs: check a portable proof with only the issuer's public key."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "rudimentall1" }]
|
|
14
|
+
keywords = ["ai-agents", "authorization", "verification", "ed25519", "audit", "x402"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Security :: Cryptography",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["cryptography>=48.0.1,<51"]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/rudimentall1/verigate"
|
|
25
|
+
Source = "https://github.com/rudimentall1/verigate/tree/main/packaging/verigate-verify"
|
|
26
|
+
Issues = "https://github.com/rudimentall1/verigate/issues"
|
|
27
|
+
"Proof format" = "https://github.com/rudimentall1/verigate/blob/main/docs/verigate-authority-proof-v1.md"
|
|
28
|
+
"Threat model" = "https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
verigate-verify = "verigate_verify._verifier:main"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import argparse,base64,hashlib,json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from cryptography.exceptions import InvalidSignature
|
|
6
|
+
from cryptography.hazmat.primitives import serialization
|
|
7
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
|
8
|
+
PROTOCOL='verigate-authority-proof-v1'; PKG='verigate-proof-package-v1'; MEDIA='application/vnd.verigate.proof-package+json'; ZERO='0'*64
|
|
9
|
+
REQUIRED_AUTHORITY_TYPES=('action_intent','execution_authorization','execution_receipt','outcome_claim','observed_effect','outcome_attestation','authority_event','authority_state','authority_state_after','authority_ledger','genesis_authority','capability')
|
|
10
|
+
def canon(x): return json.dumps(x,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()
|
|
11
|
+
def sha(x): return hashlib.sha256(canon(x)).hexdigest()
|
|
12
|
+
def b64(x): return base64.b64decode(x,validate=True)
|
|
13
|
+
def ref(n): return f"{n['type']}:{n['id']}"
|
|
14
|
+
def node(p,k):
|
|
15
|
+
x=[n for n in p.get('nodes',[]) if n.get('type')==k]; return x[0] if len(x)==1 else None
|
|
16
|
+
def edge(p,a,r,b): return any(e.get('from')==a and e.get('relation')==r and e.get('to')==b for e in p.get('edges',[]))
|
|
17
|
+
def merkle(xs):
|
|
18
|
+
if not xs:return hashlib.sha256(b'VERIGATE-EVIDENCE-MERKLE:v1:empty').hexdigest()
|
|
19
|
+
xs=sorted(xs)
|
|
20
|
+
while len(xs)>1:
|
|
21
|
+
if len(xs)%2:xs.append(xs[-1])
|
|
22
|
+
xs=[hashlib.sha256(('pair:'+xs[i]+':'+xs[i+1]).encode()).hexdigest() for i in range(0,len(xs),2)]
|
|
23
|
+
return xs[0]
|
|
24
|
+
def root(p):
|
|
25
|
+
xs=[hashlib.sha256(('node:'+sha(n['data'])).encode()).hexdigest() for n in p.get('nodes',[])]
|
|
26
|
+
xs += [hashlib.sha256(('edge:'+sha(e)).encode()).hexdigest() for e in p.get('edges',[])]
|
|
27
|
+
return merkle(xs)
|
|
28
|
+
def bad(r,c): return {'valid':False,'protocol':PROTOCOL,'reason':r,'checks':c}
|
|
29
|
+
def assertions(m):
|
|
30
|
+
p=m['payload']; kinds=['identity','capability','action_intent','authority_state','genesis_authority','decision','execution_authorization','execution_receipt','outcome_claim','observed_effect','outcome_attestation','authority_event','authority_state_after','authority_ledger']; n={k:node(p,k) for k in kinds}
|
|
31
|
+
if any(v is None for v in n.values()): return False,'authority protocol evidence incomplete'
|
|
32
|
+
R={k:ref(v) for k,v in n.items()}; ap=n['execution_authorization']['data']['payload']; rp=n['execution_receipt']['data']['payload']; cp=n['outcome_claim']['data']; oe=n['observed_effect']['data']; ep=n['authority_event']['data']; sp=n['authority_state_after']['data']; ent=n['authority_ledger']['data'].get('entries',[])
|
|
33
|
+
def A(i,k,s,pr,o,e):
|
|
34
|
+
x={'id':i,'kind':k,'subject':s,'predicate':pr,'object':o,'evidence':e}; x['sha256']=hashlib.sha256(canon(x)).hexdigest(); return x
|
|
35
|
+
exp=[A('A1','identity',R['identity'],'possessed_capability',R['capability'],[R['identity'],R['capability']]),A('A2','intent',R['action_intent'],'proposed_by',n['identity']['data'].get('agent_id',p.get('agent_id')),[R['action_intent'],R['identity']]),A('A3','authority',R['authority_state'],'permitted',R['action_intent'],[R['authority_state'],R['action_intent'],R['capability']]),A('A4','authorization',R['execution_authorization'],'derived_from',R['authority_state'],[R['decision'],R['execution_authorization'],R['authority_state'],R['genesis_authority']]),A('A5','execution',R['execution_receipt'],'consumed_authorization',R['execution_authorization'],[R['execution_authorization'],R['execution_receipt']]),A('A6','observation',R['outcome_claim'],'observes',R['execution_receipt'],[R['execution_receipt'],R['outcome_claim'],R['observed_effect'],R['outcome_attestation']]),A('A7','learning',R['authority_event'],'justified_by',R['outcome_claim'],[R['outcome_claim'],R['authority_event']]),A('A8','learning',R['authority_state_after'],'produced_by',R['authority_event'],[R['authority_event'],R['authority_state_after']]),A('A9','integrity',R['authority_state_after'],'ledger_bound',R['authority_ledger'],[R['authority_state_after'],R['authority_ledger']])]
|
|
36
|
+
astate=n['authority_state']['data']; capdata=n['capability']['data']; gd=n['genesis_authority']['data']
|
|
37
|
+
authority_digest=sha({'authority_id':f"{ap.get('agent_id')}:{n['capability']['id']}:{astate.get('ledger_head_hash') or sha(astate)}",'agent_id':ap.get('agent_id'),'identity_id':ap.get('identity_id',''),'capability_id':n['capability']['id'],'capability_version':capdata.get('version',1),'capability_sha256':ap.get('capability_sha256',''),'state':astate.get('state'),'multiplier':float(astate.get('multiplier')),'epoch':astate.get('epoch',0),'ledger_head_hash':astate.get('ledger_head_hash',''),'effective_from':float(astate.get('evaluated_at',0.0)),'expires_at':None,'parent_authority_id':None,'evidence_refs':(),'metadata':{'source':'core.authority_state.AuthoritySnapshot','successes':astate.get('successes',0),'adverse_events':astate.get('adverse_events',0),'critical_events':astate.get('critical_events',0),'reason':astate.get('reason','')}})
|
|
38
|
+
checks=[edge(p,R['identity'],'AUTHENTICATES',R['action_intent']),edge(p,R['capability'],'AUTHORIZES',R['action_intent']),ap.get('authority_state_sha256')==sha(n['authority_state']['data']),gd.get('authority_digest')==authority_digest,edge(p,R['decision'],'MINTS',R['execution_authorization']),edge(p,R['execution_authorization'],'PRODUCES',R['execution_receipt']),rp.get('authorization_id')==ap.get('authorization_id'),edge(p,R['execution_receipt'],'OBSERVED_BY',R['outcome_claim']),edge(p,R['outcome_claim'],'SUPPORTED_BY',R['observed_effect']),oe.get('authorization_id')==ap.get('authorization_id'),oe.get('action_sha256')==cp.get('action_sha256'),oe.get('evidence_ref')==cp.get('evidence_ref'),oe.get('result_sha256')==cp.get('result_sha256'),cp.get('observed_effect_sha256')==sha(oe),oe.get('observation_sha256')==sha({'verifier_type':oe.get('verifier_type'),'evidence_kind':oe.get('evidence_kind'),'effect_status':oe.get('effect_status'),'authorization_id':oe.get('authorization_id'),'action_sha256':oe.get('action_sha256'),'observed_at':oe.get('observed_at'),'evidence_ref':oe.get('evidence_ref'),'result_sha256':oe.get('result_sha256'),'observation':oe.get('observation')}),edge(p,R['outcome_attestation'],'ATTESTS',R['outcome_claim']),edge(p,R['outcome_claim'],'INFORMS',R['authority_event']),edge(p,R['authority_event'],'TRANSITIONS_TO',R['authority_state_after']),ep.get('evidence_ref')==cp.get('claim_id'),sp.get('source_event_id')==ep.get('event_id'),len([x for x in ent if x.get('event_id')==ep.get('event_id')])==1 and sp.get('ledger_head_hash')==next((x.get('event_hash') for x in ent if x.get('event_id')==ep.get('event_id')),None)]
|
|
39
|
+
return (exp==p.get('authority_assertions') and all(checks) and sha(exp)==p.get('authority_assertions_sha256'),'signed authority assertions do not match evidence' if exp!=p.get('authority_assertions') else 'authority protocol preconditions failed')
|
|
40
|
+
def load_trusted_key(path):
|
|
41
|
+
raw=Path(path).read_bytes()
|
|
42
|
+
public_key=serialization.load_pem_public_key(raw)
|
|
43
|
+
if not isinstance(public_key,Ed25519PublicKey): raise ValueError('issuer public key must be Ed25519')
|
|
44
|
+
return base64.b64encode(public_key.public_bytes(serialization.Encoding.Raw,serialization.PublicFormat.Raw)).decode()
|
|
45
|
+
|
|
46
|
+
def verify_historical(snapshot,entries,policy,expected_head):
|
|
47
|
+
try:
|
|
48
|
+
eval_at=float(snapshot['evaluated_at']); hist=float(snapshot['history_start_at']); win=float(policy['window_seconds']); p=int(policy['probation_successes']); s=int(policy['standard_successes']); l=int(policy['limited_adverse_events']); c=int(policy['suspension_critical_events']); mult={'PROBATION':float(policy['probation_multiplier']),'LIMITED':float(policy['limited_multiplier']),'STANDARD':float(policy['standard_multiplier']),'ELEVATED':float(policy['elevated_multiplier']),'SUSPENDED':0.0}
|
|
49
|
+
except Exception:return False,'historical authority policy or snapshot is malformed'
|
|
50
|
+
if hist!=eval_at-win:return False,'historical authority history boundary is not policy-derived'
|
|
51
|
+
if snapshot.get('authority_policy_sha256')!=sha(policy):return False,'historical authority policy binding mismatch'
|
|
52
|
+
committed=[]
|
|
53
|
+
if expected_head!='0'*64:
|
|
54
|
+
for x in entries:
|
|
55
|
+
committed.append(x)
|
|
56
|
+
if x.get('event_hash')==expected_head:break
|
|
57
|
+
if not committed or committed[-1].get('event_hash')!=expected_head:return False,'historical authority ledger head is not present'
|
|
58
|
+
for x in entries[len(committed):]:
|
|
59
|
+
e=x.get('event') if isinstance(x,dict) else None
|
|
60
|
+
if not isinstance(e,dict):return False,'historical authority future event is malformed'
|
|
61
|
+
t=e.get('occurred_at')
|
|
62
|
+
if not isinstance(t,(int,float)) or isinstance(t,bool):return False,'historical authority future event timestamp is invalid'
|
|
63
|
+
if float(t)<=eval_at:return False,'historical authority head is inconsistent with evaluation time'
|
|
64
|
+
succ=adv=crit=0; advset={'EXECUTION_FAILED','AUTHORIZATION_REJECTED','POLICY_VIOLATION','TAMPER_DETECTED'}; critset={'TAMPER_DETECTED','POLICY_VIOLATION'}
|
|
65
|
+
for row in committed:
|
|
66
|
+
e=row.get('event') if isinstance(row,dict) else None
|
|
67
|
+
if not isinstance(e,dict):return False,'historical authority ledger contains malformed event'
|
|
68
|
+
t=e.get('occurred_at')
|
|
69
|
+
if not isinstance(t,(int,float)) or isinstance(t,bool):return False,'historical authority event timestamp is invalid'
|
|
70
|
+
if hist<=float(t)<=eval_at:
|
|
71
|
+
succ+=1 if e.get('event_type')=='EXECUTION_CONFIRMED' else 0; adv+=1 if e.get('event_type') in advset else 0; crit+=1 if e.get('event_type') in critset else 0
|
|
72
|
+
if (snapshot.get('successes'),snapshot.get('adverse_events'),snapshot.get('critical_events'))!=(succ,adv,crit):return False,'historical authority counters do not match ledger history'
|
|
73
|
+
if crit>=c:state='SUSPENDED';reason='critical authority event observed'
|
|
74
|
+
elif adv>=l:state='LIMITED';reason='adverse outcome threshold reached'
|
|
75
|
+
elif succ>=s and adv<=1:state='ELEVATED';reason='verified success threshold reached with bounded adverse history'
|
|
76
|
+
elif succ>=p and adv<=1:state='STANDARD';reason='probation success threshold reached'
|
|
77
|
+
else:state='PROBATION';reason='insufficient verified history for broader authority'
|
|
78
|
+
if snapshot.get('state')!=state:return False,'historical authority state is not deterministically justified'
|
|
79
|
+
if snapshot.get('multiplier')!=mult[state]:return False,'historical authority multiplier is inconsistent'
|
|
80
|
+
if snapshot.get('reason')!=reason:return False,'historical authority reason is inconsistent'
|
|
81
|
+
return True,'valid historical authority'
|
|
82
|
+
|
|
83
|
+
def verify_learning(claim,att,ev,before,after,entries,policy,expected_head):
|
|
84
|
+
if claim.get('status')=='SUCCEEDED': et='EXECUTION_CONFIRMED'
|
|
85
|
+
elif claim.get('status')=='FAILED': et='EXECUTION_FAILED'
|
|
86
|
+
else: return False,'authority lifecycle learning requires a terminal independent outcome'
|
|
87
|
+
agent=claim.get('agent_id'); cap=before.get('capability_id'); cid=claim.get('claim_id')
|
|
88
|
+
if ev.get('agent_id')!=agent or ev.get('capability_id')!=cap or ev.get('event_type')!=et or ev.get('evidence_ref')!=cid:return False,'authority event does not match canonical outcome'
|
|
89
|
+
if ev.get('event_id')!=hashlib.sha256(f"{agent}:{cap}:{et}:{cid}".encode()).hexdigest():return False,'authority event id is not deterministic'
|
|
90
|
+
meta=ev.get('metadata') if isinstance(ev.get('metadata'),dict) else {}
|
|
91
|
+
if 'outcome_attestation_id' in meta and meta.get('outcome_attestation_id')!=att.get('attestation_id'):return False,'authority event is not bound to outcome attestation'
|
|
92
|
+
if 'attestation_type' in meta and meta.get('attestation_type')!=att.get('attestor_type'):return False,'authority event attestation type is inconsistent'
|
|
93
|
+
try:
|
|
94
|
+
eval_at=float(after['evaluated_at']); hist=float(after['history_start_at']); win=float(policy['window_seconds'])
|
|
95
|
+
thresholds=(int(policy['probation_successes']),int(policy['standard_successes']),int(policy['limited_adverse_events']),int(policy['suspension_critical_events']))
|
|
96
|
+
mult={'PROBATION':float(policy['probation_multiplier']),'LIMITED':float(policy['limited_multiplier']),'STANDARD':float(policy['standard_multiplier']),'ELEVATED':float(policy['elevated_multiplier']),'SUSPENDED':0.0}
|
|
97
|
+
except Exception:return False,'authority policy or post-learning snapshot is malformed'
|
|
98
|
+
if hist!=eval_at-win:return False,'post-learning authority history boundary is not policy-derived'
|
|
99
|
+
if after.get('authority_policy_sha256')!=sha(policy):return False,'post-learning authority policy binding mismatch'
|
|
100
|
+
if after.get('agent_id')!=agent or after.get('capability_id')!=cap:return False,'post-learning authority snapshot identity mismatch'
|
|
101
|
+
eh=sha(ev); matches=[x for x in entries if isinstance(x,dict) and x.get('event_id')==ev.get('event_id')]
|
|
102
|
+
if len(matches)!=1 or matches[0].get('event_hash')!=eh or matches[0].get('event')!=ev:return False,'authority ledger event is not exact canonical learning evidence'
|
|
103
|
+
if after.get('ledger_head_hash')!=eh:return False,'post-learning authority is not bound to the learning event hash'
|
|
104
|
+
try: event_at=float(ev['occurred_at']); before_at=float(before['evaluated_at'])
|
|
105
|
+
except Exception:return False,'authority learning timestamps are malformed'
|
|
106
|
+
if event_at>eval_at or event_at<hist:return False,'learning event is outside the post-learning authority evaluation window'
|
|
107
|
+
if before_at>eval_at:return False,'post-learning authority is evaluated before the historical snapshot'
|
|
108
|
+
if before.get('ledger_head_hash')!=expected_head:return False,'historical authority snapshot is not bound to authorization head'
|
|
109
|
+
prior=next((x.get('sequence') for x in entries if x.get('event_hash')==expected_head),0)
|
|
110
|
+
seq=matches[0].get('sequence')
|
|
111
|
+
if expected_head!='0'*64 and prior==0:return False,'authorization ledger head is not present in authority ledger'
|
|
112
|
+
if not isinstance(seq,int) or seq<=prior:return False,'learning event does not extend authorization ledger head'
|
|
113
|
+
for x in entries:
|
|
114
|
+
if isinstance(x,dict) and isinstance(x.get('sequence'),int) and x['sequence']>seq:
|
|
115
|
+
fe=x.get('event')
|
|
116
|
+
if isinstance(fe,dict) and float(fe.get('occurred_at',eval_at+1))<=eval_at:return False,'post-learning snapshot is not bound to ledger head at evaluation time'
|
|
117
|
+
successes=adverse=critical=0; adv={'EXECUTION_FAILED','AUTHORIZATION_REJECTED','POLICY_VIOLATION','TAMPER_DETECTED'}; crit={'TAMPER_DETECTED','POLICY_VIOLATION'}
|
|
118
|
+
for row in entries:
|
|
119
|
+
e=row.get('event') if isinstance(row,dict) else None
|
|
120
|
+
if not isinstance(e,dict):return False,'authority ledger contains malformed event'
|
|
121
|
+
t=e.get('occurred_at')
|
|
122
|
+
if not isinstance(t,(int,float)) or isinstance(t,bool):return False,'authority event timestamp is invalid'
|
|
123
|
+
if hist<=float(t)<=eval_at:
|
|
124
|
+
successes+=1 if e.get('event_type')=='EXECUTION_CONFIRMED' else 0; adverse+=1 if e.get('event_type') in adv else 0; critical+=1 if e.get('event_type') in crit else 0
|
|
125
|
+
if (after.get('successes'),after.get('adverse_events'),after.get('critical_events'))!=(successes,adverse,critical):return False,'post-learning authority counters do not match ledger history'
|
|
126
|
+
p,s,l,c=thresholds; prev=before.get('state')
|
|
127
|
+
if prev=='SUSPENDED': state='SUSPENDED'; reason='suspended until an explicit authority reset'
|
|
128
|
+
elif critical>=c: state='SUSPENDED'; reason='critical authority event observed'
|
|
129
|
+
elif adverse>=l: state='LIMITED'; reason='adverse outcome threshold reached'
|
|
130
|
+
elif successes>=s and adverse<=1: state='ELEVATED'; reason='verified success threshold reached with bounded adverse history'
|
|
131
|
+
elif successes>=p and adverse<=1: state='STANDARD'; reason='probation success threshold reached'
|
|
132
|
+
else: state='PROBATION'; reason='insufficient verified history for broader authority'
|
|
133
|
+
if after.get('state')!=state:return False,'post-learning authority state is not deterministically justified'
|
|
134
|
+
if after.get('multiplier')!=mult[state]:return False,'post-learning authority multiplier is inconsistent'
|
|
135
|
+
if after.get('reason')!=reason:return False,'post-learning authority reason is inconsistent'
|
|
136
|
+
return True,'valid deterministic learning transition'
|
|
137
|
+
|
|
138
|
+
def verify(proof,key):
|
|
139
|
+
c={}; d=json.loads(Path(proof).read_text(encoding='utf-8')); trusted=load_trusted_key(key); pkg=d.get('package'); ph=d.get('package_sha256')
|
|
140
|
+
c['package_integrity']={'valid':isinstance(pkg,dict) and sha(pkg)==ph}
|
|
141
|
+
if not c['package_integrity']['valid']:return bad('package digest mismatch',c)
|
|
142
|
+
m=pkg.get('manifest'); p=m.get('payload') if isinstance(m,dict) else None
|
|
143
|
+
c['manifest_integrity']={'valid':isinstance(m,dict) and pkg.get('manifest_sha256')==sha(m)}
|
|
144
|
+
if not c['manifest_integrity']['valid']:return bad('manifest digest mismatch',c)
|
|
145
|
+
if pkg.get('protocol')!=PKG or pkg.get('media_type')!=MEDIA or not isinstance(p,dict) or p.get('proof_profile')!='authority_lifecycle':return bad('unsupported proof package',c)
|
|
146
|
+
if pkg.get('proof_protocol')!=PROTOCOL or pkg.get('protocol')!=PKG: return bad('package protocol metadata mismatch',c)
|
|
147
|
+
if not pkg.get('self_contained') or pkg.get('node_count')!=len(p.get('nodes',[])) or pkg.get('edge_count')!=len(p.get('edges',[])): return bad('package graph metadata mismatch',c)
|
|
148
|
+
inventory=[{'type':n.get('type'),'id':n.get('id'),'sha256':n.get('sha256')} for n in p.get('nodes',[])]
|
|
149
|
+
if pkg.get('node_inventory')!=inventory or pkg.get('graph_root_digest')!=p.get('root_digest'): return bad('package graph inventory mismatch',c)
|
|
150
|
+
if pkg.get('assertion_set_sha256')!=sha(pkg.get('assertions',[])): return bad('package assertion-set digest mismatch',c)
|
|
151
|
+
if p.get('proof_profile')=='authority_lifecycle' and pkg.get('required_artifact_types')!=list(REQUIRED_AUTHORITY_TYPES): return bad('authority lifecycle required artifact metadata mismatch',c)
|
|
152
|
+
if p.get('proof_profile')=='authority_lifecycle' and (pkg.get('assertions')!=p.get('authority_assertions') or pkg.get('assertion_set_sha256')!=p.get('authority_assertions_sha256')): return bad('package assertions are not bound to signed manifest',c)
|
|
153
|
+
c['trust_anchor']={'valid':m.get('issuer_public_key_b64')==trusted}
|
|
154
|
+
if not c['trust_anchor']['valid']:return bad('issuer public key is not trusted',c)
|
|
155
|
+
try: pub=Ed25519PublicKey.from_public_bytes(b64(trusted)); pub.verify(b64(m['signature']),canon(p)); sig=True
|
|
156
|
+
except Exception:sig=False
|
|
157
|
+
c['issuer_signature']={'valid':sig}
|
|
158
|
+
if not sig:return bad('invalid manifest signature',c)
|
|
159
|
+
refs=set(); good=True
|
|
160
|
+
for n in p.get('nodes',[]):
|
|
161
|
+
if not isinstance(n,dict) or not all(k in n for k in ('type','id','sha256','data')) or sha(n['data'])!=n['sha256'] or ref(n) in refs:good=False;break
|
|
162
|
+
refs.add(ref(n))
|
|
163
|
+
good=good and all(isinstance(e,dict) and all(k in e for k in ('from','relation','to')) and e['from'] in refs and e['to'] in refs for e in p.get('edges',[])) and root(p)==p.get('root_digest')
|
|
164
|
+
c['graph_integrity']={'valid':good}
|
|
165
|
+
if not good:return bad('graph integrity mismatch',c)
|
|
166
|
+
ok,why=assertions(m); c['authority_protocol']={'valid':ok}
|
|
167
|
+
if not ok:return bad(why,c)
|
|
168
|
+
a=node(p,'execution_authorization'); r=node(p,'execution_receipt'); ap=a['data']['payload']; rp=r['data']['payload']; t=rp.get('executed_at')
|
|
169
|
+
identity_node=node(p,'identity'); capability_node=node(p,'capability'); intent_node=node(p,'action_intent'); authority_node=node(p,'authority_state'); genesis_node=node(p,'genesis_authority')
|
|
170
|
+
if any(x is None for x in (identity_node,capability_node,intent_node,authority_node,genesis_node)): return bad('authority binding evidence incomplete',c)
|
|
171
|
+
if ap.get('agent_id') != p.get('agent_id') or ap.get('agent_id') != identity_node['data'].get('agent_id') or ap.get('identity_id') != identity_node['id'] or ap.get('capability_id') != capability_node['id'] or ap.get('intent_id') != intent_node['id']:
|
|
172
|
+
return bad('authorization identity/capability/intent binding failed',c)
|
|
173
|
+
if ap.get('action') != intent_node['data']:
|
|
174
|
+
return bad('execution authorization action payload differs from canonical intent',c)
|
|
175
|
+
if rp.get('authorization_sha256') != sha(ap):
|
|
176
|
+
return bad('execution receipt is not bound to the exact execution authorization payload',c)
|
|
177
|
+
external_state_required=bool(ap.get('external_state_required'))
|
|
178
|
+
external_state_scope=rp.get('execution_external_state_scope')
|
|
179
|
+
external_state=ap.get('external_state')
|
|
180
|
+
requirement=ap.get('external_state_requirement')
|
|
181
|
+
if not external_state_required:
|
|
182
|
+
if external_state_scope not in (None,'atomic'):
|
|
183
|
+
return bad('unsupported execution external-state scope',c)
|
|
184
|
+
else:
|
|
185
|
+
if external_state_scope != 'atomic':
|
|
186
|
+
return bad('required external state execution is not atomically enforced',c)
|
|
187
|
+
if not isinstance(external_state,dict) or not external_state:
|
|
188
|
+
return bad('required external state binding is missing',c)
|
|
189
|
+
if not isinstance(requirement,dict):
|
|
190
|
+
return bad('required external state policy requirement is missing',c)
|
|
191
|
+
metadata=intent_node['data'].get('metadata')
|
|
192
|
+
canonical_state=metadata.get('external_state') if isinstance(metadata,dict) else None
|
|
193
|
+
if not isinstance(canonical_state,dict) or external_state != canonical_state:
|
|
194
|
+
return bad('external state binding differs from canonical intent',c)
|
|
195
|
+
if ap.get('external_state_sha256') != sha(external_state):
|
|
196
|
+
return bad('external state fingerprint mismatch',c)
|
|
197
|
+
if ap.get('external_state_requirement_sha256') != sha(requirement):
|
|
198
|
+
return bad('external state requirement fingerprint mismatch',c)
|
|
199
|
+
if external_state.get('kind') != requirement.get('kind'):
|
|
200
|
+
return bad('external state kind does not satisfy policy requirement',c)
|
|
201
|
+
if not isinstance(external_state.get('reference'),str) or not external_state['reference'].strip():
|
|
202
|
+
return bad('external state binding is missing reference',c)
|
|
203
|
+
dv=external_state.get('digest')
|
|
204
|
+
if not isinstance(dv,str) or len(dv)!=64:
|
|
205
|
+
return bad('external state binding has invalid digest',c)
|
|
206
|
+
try: int(dv,16)
|
|
207
|
+
except Exception: return bad('external state binding digest is not hexadecimal',c)
|
|
208
|
+
authority_data=authority_node['data']; authority_sha=sha(authority_data)
|
|
209
|
+
if ap.get('authority_state_sha256') != authority_sha: return bad('historical authority snapshot digest mismatch',c)
|
|
210
|
+
capability_data=capability_node['data']
|
|
211
|
+
authority_obj={'authority_id':f"{ap['agent_id']}:{capability_node['id']}:{authority_data.get('ledger_head_hash') or sha(authority_data)}",'agent_id':ap['agent_id'],'identity_id':ap.get('identity_id',''),'capability_id':capability_node['id'],'capability_version':capability_data.get('version',1),'capability_sha256':ap.get('capability_sha256',''),'state':authority_data['state'],'multiplier':float(authority_data['multiplier']),'epoch':0,'ledger_head_hash':authority_data.get('ledger_head_hash',''),'effective_from':float(authority_data.get('evaluated_at',0.0)),'expires_at':None,'parent_authority_id':None,'evidence_refs':[],'metadata':{'source':'core.authority_state.AuthoritySnapshot','successes':authority_data.get('successes',0),'adverse_events':authority_data.get('adverse_events',0),'critical_events':authority_data.get('critical_events',0),'reason':authority_data.get('reason','')}}
|
|
212
|
+
if genesis_node['data'].get('authority_digest') != sha(authority_obj): return bad('Genesis authority does not bind historical authority',c)
|
|
213
|
+
try:pub.verify(b64(a['data']['signature']),canon(ap)); asig=True
|
|
214
|
+
except Exception:asig=False
|
|
215
|
+
c['authorization']={'valid':asig and ap.get('expires_at',-1)>=t>=ap.get('issued_at',10**30),'historical_verification_time':t}
|
|
216
|
+
if not c['authorization']['valid']:return bad('execution authorization verification failed',c)
|
|
217
|
+
try:pub.verify(b64(r['data']['signature']),canon(rp)); rsig=True
|
|
218
|
+
except Exception:rsig=False
|
|
219
|
+
c['execution']={'valid':rsig and rp.get('authorization_id')==ap.get('authorization_id') and rp.get('intent_id')==ap.get('intent_id')}
|
|
220
|
+
if not c['execution']['valid']:return bad('execution receipt verification failed',c)
|
|
221
|
+
l=node(p,'authority_ledger')['data'].get('entries',[]); prev=ZERO; lok=True
|
|
222
|
+
for i,x in enumerate(l,1):
|
|
223
|
+
if x.get('sequence')!=i or x.get('prev_event_hash')!=prev or hashlib.sha256(canon(x.get('event'))).hexdigest()!=x.get('event_hash'):lok=False;break
|
|
224
|
+
prev=x.get('event_hash')
|
|
225
|
+
head=ap.get('authority_ledger_head_hash'); c['historical_authority']={'valid':lok and (head=='0'*64 or any(x.get('event_hash')==head for x in l)),'ledger_sequence':next((i for i,x in enumerate(l,1) if x.get('event_hash')==head),0),'live_ledger_sequence':len(l)}
|
|
226
|
+
if not c['historical_authority']['valid']:return bad('historical authority binding failed',c)
|
|
227
|
+
h_ok,h_reason=verify_historical(authority_data,l,ap.get('authority_policy') or {},head); c['historical_authority_semantics']={'valid':h_ok,'reason':h_reason}
|
|
228
|
+
if not h_ok:return bad('historical authority semantics failed',c)
|
|
229
|
+
cl=node(p,'outcome_claim'); oe=node(p,'observed_effect'); at=node(p,'outcome_attestation'); ev=node(p,'authority_event'); af=node(p,'authority_state_after'); claim_data=cl['data']; observed_data=oe['data']; att_payload=at['data'].get('payload',{}); out=edge(p,ref(r),'OBSERVED_BY',ref(cl)) and edge(p,ref(cl),'SUPPORTED_BY',ref(oe)) and claim_data.get('execution_receipt_sha256')==sha(r['data']) and claim_data.get('observed_effect_sha256')==sha(observed_data) and oe['id']==claim_data.get('observed_effect_sha256') and observed_data.get('authorization_id')==ap.get('authorization_id') and observed_data.get('action_sha256')==claim_data.get('action_sha256') and observed_data.get('evidence_ref')==claim_data.get('evidence_ref') and observed_data.get('result_sha256')==claim_data.get('result_sha256') and observed_data.get('observation_sha256')==sha({'verifier_type':observed_data.get('verifier_type'),'evidence_kind':observed_data.get('evidence_kind'),'effect_status':observed_data.get('effect_status'),'authorization_id':observed_data.get('authorization_id'),'action_sha256':observed_data.get('action_sha256'),'observed_at':observed_data.get('observed_at'),'evidence_ref':observed_data.get('evidence_ref'),'result_sha256':observed_data.get('result_sha256'),'observation':observed_data.get('observation')}) and edge(p,ref(at),'ATTESTS',ref(cl)) and att_payload.get('claim_sha256')==sha(claim_data) and att_payload.get('claim',{}).get('claim_id')==claim_data.get('claim_id'); c['outcome']={'valid':out}; learn_ok,learn_reason=verify_learning(claim_data,att_payload,ev['data'],authority_node['data'],af['data'],l,ap.get('authority_policy') or {},ap.get('authority_ledger_head_hash','')); learn=learn_ok; c['learning']={'valid':learn,'reason':learn_reason}
|
|
230
|
+
if not out or not learn:return bad('outcome or learning binding failed',c)
|
|
231
|
+
eh=next((x.get('event_hash') for x in l if x.get('event_id')==ev['data'].get('event_id')),None); c['post_learning_authority']={'valid':eh is not None and af['data'].get('ledger_head_hash')==eh}
|
|
232
|
+
if not c['post_learning_authority']['valid']:return bad('post-learning authority is not ledger-bound',c)
|
|
233
|
+
return {'valid':True,'protocol':PROTOCOL,'root_digest':p['root_digest'],'package_sha256':ph,'assertion_set_sha256':pkg.get('assertion_set_sha256'),'checks':c}
|
|
234
|
+
def main():
|
|
235
|
+
x=argparse.ArgumentParser();x.add_argument('proof');x.add_argument('--public-key',required=True);x.add_argument('--format',choices=['text','json'],default='text');a=x.parse_args()
|
|
236
|
+
try:
|
|
237
|
+
r=verify(a.proof,a.public_key)
|
|
238
|
+
except Exception as exc:
|
|
239
|
+
r=bad('verification failed closed: '+str(exc),{})
|
|
240
|
+
if a.format=='json':print(json.dumps(r,indent=2,sort_keys=True))
|
|
241
|
+
else:
|
|
242
|
+
print('VERIGATE AUTHORITY PROOF - STANDALONE');print('Protocol:',r.get('protocol',PROTOCOL));[print(f"{k.upper():28} {'PASS' if v.get('valid') else 'FAIL'}") for k,v in r.get('checks',{}).items()];print('RESULT:', 'VALID' if r.get('valid') else 'INVALID'); print('Reason:',r['reason']) if not r.get('valid') else None
|
|
243
|
+
return 0 if r.get('valid') else 1
|
|
244
|
+
if __name__=='__main__':raise SystemExit(main())
|
|
245
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: verigate-verify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Offline verifier for Verigate authority proofs: check a portable proof with only the issuer's public key.
|
|
5
|
+
Author: rudimentall1
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rudimentall1/verigate
|
|
8
|
+
Project-URL: Source, https://github.com/rudimentall1/verigate/tree/main/packaging/verigate-verify
|
|
9
|
+
Project-URL: Issues, https://github.com/rudimentall1/verigate/issues
|
|
10
|
+
Project-URL: Proof format, https://github.com/rudimentall1/verigate/blob/main/docs/verigate-authority-proof-v1.md
|
|
11
|
+
Project-URL: Threat model, https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md
|
|
12
|
+
Keywords: ai-agents,authorization,verification,ed25519,audit,x402
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: cryptography<51,>=48.0.1
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# verigate-verify
|
|
24
|
+
|
|
25
|
+
Offline verifier for [Verigate](https://github.com/rudimentall1/verigate) authority proofs.
|
|
26
|
+
|
|
27
|
+
Verigate decides, per action, whether an AI agent had the authority to act, issues a one-time signed
|
|
28
|
+
authorization, and exports a portable proof. This package is the standalone verifier for that proof:
|
|
29
|
+
**you need only the proof file and the issuer's Ed25519 public key.** No Verigate server, database or API.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install verigate-verify
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Use
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
verigate-verify proof.json --public-key issuer.pub
|
|
41
|
+
verigate-verify proof.json --public-key issuer.pub --format json
|
|
42
|
+
# or
|
|
43
|
+
python -m verigate_verify proof.json --public-key issuer.pub
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The result is `VALID` or `INVALID` with a reason (for example `proof package digest mismatch` if the
|
|
47
|
+
evidence was altered). Exit code is 0 for a valid proof and non-zero otherwise.
|
|
48
|
+
|
|
49
|
+
A sample proof and issuer key are in the repository under `examples/authority-proof/`.
|
|
50
|
+
|
|
51
|
+
## What it does and does not tell you
|
|
52
|
+
|
|
53
|
+
It checks the package digest, the evidence manifest, the issuer signature against the public key you trust,
|
|
54
|
+
the evidence graph and the authority protocol assertions. It does not decide whether the issuer's key is the
|
|
55
|
+
right one to trust (you must obtain the public key through a channel you trust), and it does not prove that no
|
|
56
|
+
other actions were taken. See the [threat model](https://github.com/rudimentall1/verigate/blob/main/docs/threat-model.md).
|
|
57
|
+
|
|
58
|
+
## Source
|
|
59
|
+
|
|
60
|
+
This is the same code as `tools/verigate_proof_verifier.py` in the main repository. MIT licensed.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/verigate_verify/__init__.py
|
|
5
|
+
src/verigate_verify/__main__.py
|
|
6
|
+
src/verigate_verify/_verifier.py
|
|
7
|
+
src/verigate_verify.egg-info/PKG-INFO
|
|
8
|
+
src/verigate_verify.egg-info/SOURCES.txt
|
|
9
|
+
src/verigate_verify.egg-info/dependency_links.txt
|
|
10
|
+
src/verigate_verify.egg-info/entry_points.txt
|
|
11
|
+
src/verigate_verify.egg-info/requires.txt
|
|
12
|
+
src/verigate_verify.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cryptography<51,>=48.0.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
verigate_verify
|