nenrin-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.
- nenrin_verify-0.1.0/LICENSE +21 -0
- nenrin_verify-0.1.0/MANIFEST.in +3 -0
- nenrin_verify-0.1.0/PKG-INFO +110 -0
- nenrin_verify-0.1.0/README.md +90 -0
- nenrin_verify-0.1.0/pyproject.toml +36 -0
- nenrin_verify-0.1.0/setup.cfg +4 -0
- nenrin_verify-0.1.0/src/nenrin_verify/VENDORED.json +12 -0
- nenrin_verify-0.1.0/src/nenrin_verify/__init__.py +44 -0
- nenrin_verify-0.1.0/src/nenrin_verify/_js.py +505 -0
- nenrin_verify-0.1.0/src/nenrin_verify/agreement_verify.py +1243 -0
- nenrin_verify-0.1.0/src/nenrin_verify/cli.py +73 -0
- nenrin_verify-0.1.0/src/nenrin_verify/key_succession.py +140 -0
- nenrin_verify-0.1.0/src/nenrin_verify/provenance.py +977 -0
- nenrin_verify-0.1.0/src/nenrin_verify/py.typed +0 -0
- nenrin_verify-0.1.0/src/nenrin_verify/selftest.json +1 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/PKG-INFO +110 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/SOURCES.txt +31 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/dependency_links.txt +1 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/entry_points.txt +3 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/requires.txt +1 -0
- nenrin_verify-0.1.0/src/nenrin_verify.egg-info/top_level.txt +1 -0
- nenrin_verify-0.1.0/tests/fixtures/bundles.json +1946 -0
- nenrin_verify-0.1.0/tests/fixtures/js_reports.json +285 -0
- nenrin_verify-0.1.0/tests/fixtures/make_fixtures.mjs +132 -0
- nenrin_verify-0.1.0/tests/parity/js_canon.mjs +11 -0
- nenrin_verify-0.1.0/tests/parity/js_runner.mjs +24 -0
- nenrin_verify-0.1.0/tests/parity/mutate.py +71 -0
- nenrin_verify-0.1.0/tests/test_agreement_frozen.py +37 -0
- nenrin_verify-0.1.0/tests/test_js_semantics_live.py +54 -0
- nenrin_verify-0.1.0/tests/test_parity_frozen.py +58 -0
- nenrin_verify-0.1.0/tests/test_parity_live.py +72 -0
- nenrin_verify-0.1.0/tests/test_vendored.py +24 -0
- nenrin_verify-0.1.0/tools/vendor.py +67 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The HORIZONs Co., Ltd.
|
|
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,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nenrin-verify
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Recompute NENRIN evidence yourself, offline: A2A task provenance and two-party agreement records. Same reports as the npm nenrin-verify.
|
|
5
|
+
Author: The HORIZONs Co., Ltd.
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ogasurfproject-jpg/horizon-shield/tree/main/workers/hs-ledger/nenrin/sdk-python
|
|
8
|
+
Project-URL: Source, https://github.com/ogasurfproject-jpg/horizon-shield
|
|
9
|
+
Project-URL: JavaScript twin, https://www.npmjs.com/package/nenrin-verify
|
|
10
|
+
Keywords: a2a,provenance,verification,did-key,ed25519,nenrin,agent,evidence,horizon-shield
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Security :: Cryptography
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: cryptography>=41
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# nenrin-verify (Python)
|
|
22
|
+
|
|
23
|
+
Recompute NENRIN evidence yourself, offline, in your own Python process. The Python twin of the npm package
|
|
24
|
+
[`nenrin-verify`](https://www.npmjs.com/package/nenrin-verify): same input, same report (the two limits are listed
|
|
25
|
+
under "Where the claim stops").
|
|
26
|
+
|
|
27
|
+
pip install nenrin-verify
|
|
28
|
+
nenrin-verify --selftest # 31/31 frozen bundles give the same report as npm nenrin-verify
|
|
29
|
+
nenrin-verify bundle.json # the provenance report; exit 0 accepted, 1 refused
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import json, nenrin_verify as nv
|
|
33
|
+
|
|
34
|
+
bundle = nv.js_loads(open("bundle.json", "rb").read()) # parsed the way JSON.parse parses it
|
|
35
|
+
report = nv.verify_bundle(bundle) # did:key resolution, no network, no clock
|
|
36
|
+
print(report["verdict"], report["does_not_establish"])
|
|
37
|
+
print(nv.report_json(report)) # JSON text as the JavaScript CLI prints it (not json.dumps)
|
|
38
|
+
print(nv.report_sha256(report)) # compare with the JavaScript run, byte for byte
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
One dependency, `cryptography`, for Ed25519. Nothing here opens a socket.
|
|
42
|
+
|
|
43
|
+
## What it verifies
|
|
44
|
+
|
|
45
|
+
| module | what | held to |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `verify_provenance`, `consume_evidence`, `posture_line`, `candidate_evidence_set`, `preflight_report` | one A2A task's provenance graph: the delegation chain observed by third-party witnesses (R1 to R4, witness and edge signatures), the caller's grant and the provider's execution receipt (E1 to E3, caller and provider signatures), the provider's pre-execution intent, the outcome's evidence pointer, and the digest link between the layers | npm nenrin-verify 0.2.3 (`nenrin_verify.mjs`, verifier 0.1.3): the same report, key for key |
|
|
48
|
+
| `agreement_verify.verify`, `nenrin-agreement-verify` | a two-party agreement record (`a2a-agreement-v1`, `v1.1`), including key succession across a rotation | the repository's own Python verifier, unchanged but for one import line and a header comment; it and the JavaScript verifier return the same report on 5,286 frozen cases |
|
|
49
|
+
|
|
50
|
+
## How "same report" is checked
|
|
51
|
+
|
|
52
|
+
The JavaScript file is the reference. The port reads it line for line, and every place where Python and
|
|
53
|
+
JavaScript disagree by default (`x or y` on `{}`, `==` on `True` and `1`, `Date.parse` accepting February 31st,
|
|
54
|
+
Node's lenient base64, an own `__proto__` key that Object.assign turns into a prototype, the order V8's sort
|
|
55
|
+
gives values a numeric comparator cannot order) goes through one file,
|
|
56
|
+
`src/nenrin_verify/_js.py`, so the differences can be read in one place.
|
|
57
|
+
|
|
58
|
+
- **Frozen.** 31 bundles signed with keys derived from a public phrase, covering accepted graphs, refusals of every
|
|
59
|
+
layer, witness disagreement, provider equivocation, action bindings, non-ASCII and escapes, and the date edge
|
|
60
|
+
cases. For each, the report, the `consume_evidence` projection and the CLI output are byte-identical to the
|
|
61
|
+
JavaScript's. They ship in the package; `nenrin-verify --selftest` re-runs them on your machine.
|
|
62
|
+
- **Live.** Every path of every frozen bundle is broken in about twenty ways (deleted, nulled, retyped, shortened,
|
|
63
|
+
reordered, timestamps and sequence numbers bent, own `__proto__` keys added, the whole bundle replaced by an
|
|
64
|
+
array or null), 27,062 inputs in all. Each is run through the JavaScript verifier and through this port. Pass
|
|
65
|
+
means both threw, or both returned the same report and projection. 0 differ.
|
|
66
|
+
- **Semantics.** The helpers in `_js.py` against Node directly: number text, Date.parse, Buffer base64, string
|
|
67
|
+
escaping and mixed-type sorting, several thousand random values each.
|
|
68
|
+
- **Agreement.** The packaged agreement verifier returns every one of the 5,286 frozen reports in
|
|
69
|
+
`agreement-v0/agreement_vectors_v1.json`, the file the JavaScript verifier is scored against.
|
|
70
|
+
- **Unchanged.** `VENDORED.json` pins the sha256 of the agreement files and their sources; a copy that drifts fails.
|
|
71
|
+
|
|
72
|
+
`report_sha256(report)` is the sha256 of the report with keys sorted by UTF-16 code unit at every depth, no
|
|
73
|
+
whitespace, strings and numbers as JSON.stringify writes them. The JavaScript side of the same hash is
|
|
74
|
+
`tests/parity/js_canon.mjs` (twelve lines).
|
|
75
|
+
|
|
76
|
+
## Where the claim stops
|
|
77
|
+
|
|
78
|
+
- **Stack depth.** A document nested deep enough to exhaust a runtime's stack has no report in that runtime, and
|
|
79
|
+
Node and Python run out at different depths (Node threw at 3,000 nested arrays inside a signed field, Python's
|
|
80
|
+
default limit is 1,000). Real bundles are a few levels deep.
|
|
81
|
+
- **Sixty-four or more non-numeric `hop.seq` values in one bundle.** The order of those keys is V8's merge sort over
|
|
82
|
+
a comparator that cannot order them; the port reproduces V8 for up to 63 keys and raises above that instead of
|
|
83
|
+
guessing.
|
|
84
|
+
- Where the JavaScript throws, the port raises; the CLI then prints no report and exits 2 (Node exits 1 on an
|
|
85
|
+
uncaught throw).
|
|
86
|
+
|
|
87
|
+
## What it does not establish
|
|
88
|
+
|
|
89
|
+
What every report says itself, accepted or refused: a signature proves who asserted, not that the assertion is
|
|
90
|
+
true; E1 compares a provider's signed claim to a caller's signed authorization and has no side-effect oracle;
|
|
91
|
+
R1 proves a witness is structurally distinct from the parties, not unaffiliated with them. There is no score and
|
|
92
|
+
no allow or deny anywhere in this package. The decision belongs to whoever reads the evidence.
|
|
93
|
+
|
|
94
|
+
Not in this release: the TSUGI recovery-chain verifier (`tsugi_verify.mjs`) and the MUSUBI contract spine. Each
|
|
95
|
+
comes in when it can carry the same guarantee as the two verifiers above: the same report as its JavaScript twin,
|
|
96
|
+
checked case by case.
|
|
97
|
+
|
|
98
|
+
## Reproduce
|
|
99
|
+
|
|
100
|
+
cd workers/hs-ledger/nenrin/sdk-python
|
|
101
|
+
pip install -e . pytest
|
|
102
|
+
node tests/fixtures/make_fixtures.mjs --check # the frozen fixtures re-create byte for byte
|
|
103
|
+
python tools/vendor.py --check # the agreement copies are their sources
|
|
104
|
+
pytest tests -q -s # frozen, live differential, agreement
|
|
105
|
+
|
|
106
|
+
Published from GitHub Actions with PyPI Trusted Publishing and attestations
|
|
107
|
+
(`.github/workflows/pypi-publish-nenrin-verify.yml`); the parity suite runs on every change to this directory,
|
|
108
|
+
to the JavaScript SDK and to the agreement verifier (`.github/workflows/nenrin-verify-py.yml`).
|
|
109
|
+
|
|
110
|
+
MIT. The HORIZONs Co., Ltd.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# nenrin-verify (Python)
|
|
2
|
+
|
|
3
|
+
Recompute NENRIN evidence yourself, offline, in your own Python process. The Python twin of the npm package
|
|
4
|
+
[`nenrin-verify`](https://www.npmjs.com/package/nenrin-verify): same input, same report (the two limits are listed
|
|
5
|
+
under "Where the claim stops").
|
|
6
|
+
|
|
7
|
+
pip install nenrin-verify
|
|
8
|
+
nenrin-verify --selftest # 31/31 frozen bundles give the same report as npm nenrin-verify
|
|
9
|
+
nenrin-verify bundle.json # the provenance report; exit 0 accepted, 1 refused
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import json, nenrin_verify as nv
|
|
13
|
+
|
|
14
|
+
bundle = nv.js_loads(open("bundle.json", "rb").read()) # parsed the way JSON.parse parses it
|
|
15
|
+
report = nv.verify_bundle(bundle) # did:key resolution, no network, no clock
|
|
16
|
+
print(report["verdict"], report["does_not_establish"])
|
|
17
|
+
print(nv.report_json(report)) # JSON text as the JavaScript CLI prints it (not json.dumps)
|
|
18
|
+
print(nv.report_sha256(report)) # compare with the JavaScript run, byte for byte
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
One dependency, `cryptography`, for Ed25519. Nothing here opens a socket.
|
|
22
|
+
|
|
23
|
+
## What it verifies
|
|
24
|
+
|
|
25
|
+
| module | what | held to |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `verify_provenance`, `consume_evidence`, `posture_line`, `candidate_evidence_set`, `preflight_report` | one A2A task's provenance graph: the delegation chain observed by third-party witnesses (R1 to R4, witness and edge signatures), the caller's grant and the provider's execution receipt (E1 to E3, caller and provider signatures), the provider's pre-execution intent, the outcome's evidence pointer, and the digest link between the layers | npm nenrin-verify 0.2.3 (`nenrin_verify.mjs`, verifier 0.1.3): the same report, key for key |
|
|
28
|
+
| `agreement_verify.verify`, `nenrin-agreement-verify` | a two-party agreement record (`a2a-agreement-v1`, `v1.1`), including key succession across a rotation | the repository's own Python verifier, unchanged but for one import line and a header comment; it and the JavaScript verifier return the same report on 5,286 frozen cases |
|
|
29
|
+
|
|
30
|
+
## How "same report" is checked
|
|
31
|
+
|
|
32
|
+
The JavaScript file is the reference. The port reads it line for line, and every place where Python and
|
|
33
|
+
JavaScript disagree by default (`x or y` on `{}`, `==` on `True` and `1`, `Date.parse` accepting February 31st,
|
|
34
|
+
Node's lenient base64, an own `__proto__` key that Object.assign turns into a prototype, the order V8's sort
|
|
35
|
+
gives values a numeric comparator cannot order) goes through one file,
|
|
36
|
+
`src/nenrin_verify/_js.py`, so the differences can be read in one place.
|
|
37
|
+
|
|
38
|
+
- **Frozen.** 31 bundles signed with keys derived from a public phrase, covering accepted graphs, refusals of every
|
|
39
|
+
layer, witness disagreement, provider equivocation, action bindings, non-ASCII and escapes, and the date edge
|
|
40
|
+
cases. For each, the report, the `consume_evidence` projection and the CLI output are byte-identical to the
|
|
41
|
+
JavaScript's. They ship in the package; `nenrin-verify --selftest` re-runs them on your machine.
|
|
42
|
+
- **Live.** Every path of every frozen bundle is broken in about twenty ways (deleted, nulled, retyped, shortened,
|
|
43
|
+
reordered, timestamps and sequence numbers bent, own `__proto__` keys added, the whole bundle replaced by an
|
|
44
|
+
array or null), 27,062 inputs in all. Each is run through the JavaScript verifier and through this port. Pass
|
|
45
|
+
means both threw, or both returned the same report and projection. 0 differ.
|
|
46
|
+
- **Semantics.** The helpers in `_js.py` against Node directly: number text, Date.parse, Buffer base64, string
|
|
47
|
+
escaping and mixed-type sorting, several thousand random values each.
|
|
48
|
+
- **Agreement.** The packaged agreement verifier returns every one of the 5,286 frozen reports in
|
|
49
|
+
`agreement-v0/agreement_vectors_v1.json`, the file the JavaScript verifier is scored against.
|
|
50
|
+
- **Unchanged.** `VENDORED.json` pins the sha256 of the agreement files and their sources; a copy that drifts fails.
|
|
51
|
+
|
|
52
|
+
`report_sha256(report)` is the sha256 of the report with keys sorted by UTF-16 code unit at every depth, no
|
|
53
|
+
whitespace, strings and numbers as JSON.stringify writes them. The JavaScript side of the same hash is
|
|
54
|
+
`tests/parity/js_canon.mjs` (twelve lines).
|
|
55
|
+
|
|
56
|
+
## Where the claim stops
|
|
57
|
+
|
|
58
|
+
- **Stack depth.** A document nested deep enough to exhaust a runtime's stack has no report in that runtime, and
|
|
59
|
+
Node and Python run out at different depths (Node threw at 3,000 nested arrays inside a signed field, Python's
|
|
60
|
+
default limit is 1,000). Real bundles are a few levels deep.
|
|
61
|
+
- **Sixty-four or more non-numeric `hop.seq` values in one bundle.** The order of those keys is V8's merge sort over
|
|
62
|
+
a comparator that cannot order them; the port reproduces V8 for up to 63 keys and raises above that instead of
|
|
63
|
+
guessing.
|
|
64
|
+
- Where the JavaScript throws, the port raises; the CLI then prints no report and exits 2 (Node exits 1 on an
|
|
65
|
+
uncaught throw).
|
|
66
|
+
|
|
67
|
+
## What it does not establish
|
|
68
|
+
|
|
69
|
+
What every report says itself, accepted or refused: a signature proves who asserted, not that the assertion is
|
|
70
|
+
true; E1 compares a provider's signed claim to a caller's signed authorization and has no side-effect oracle;
|
|
71
|
+
R1 proves a witness is structurally distinct from the parties, not unaffiliated with them. There is no score and
|
|
72
|
+
no allow or deny anywhere in this package. The decision belongs to whoever reads the evidence.
|
|
73
|
+
|
|
74
|
+
Not in this release: the TSUGI recovery-chain verifier (`tsugi_verify.mjs`) and the MUSUBI contract spine. Each
|
|
75
|
+
comes in when it can carry the same guarantee as the two verifiers above: the same report as its JavaScript twin,
|
|
76
|
+
checked case by case.
|
|
77
|
+
|
|
78
|
+
## Reproduce
|
|
79
|
+
|
|
80
|
+
cd workers/hs-ledger/nenrin/sdk-python
|
|
81
|
+
pip install -e . pytest
|
|
82
|
+
node tests/fixtures/make_fixtures.mjs --check # the frozen fixtures re-create byte for byte
|
|
83
|
+
python tools/vendor.py --check # the agreement copies are their sources
|
|
84
|
+
pytest tests -q -s # frozen, live differential, agreement
|
|
85
|
+
|
|
86
|
+
Published from GitHub Actions with PyPI Trusted Publishing and attestations
|
|
87
|
+
(`.github/workflows/pypi-publish-nenrin-verify.yml`); the parity suite runs on every change to this directory,
|
|
88
|
+
to the JavaScript SDK and to the agreement verifier (`.github/workflows/nenrin-verify-py.yml`).
|
|
89
|
+
|
|
90
|
+
MIT. The HORIZONs Co., Ltd.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nenrin-verify"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Recompute NENRIN evidence yourself, offline: A2A task provenance and two-party agreement records. Same reports as the npm nenrin-verify."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
authors = [{ name = "The HORIZONs Co., Ltd." }]
|
|
14
|
+
dependencies = ["cryptography>=41"]
|
|
15
|
+
keywords = ["a2a", "provenance", "verification", "did-key", "ed25519", "nenrin", "agent", "evidence", "horizon-shield"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: Security :: Cryptography",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/ogasurfproject-jpg/horizon-shield/tree/main/workers/hs-ledger/nenrin/sdk-python"
|
|
25
|
+
Source = "https://github.com/ogasurfproject-jpg/horizon-shield"
|
|
26
|
+
"JavaScript twin" = "https://www.npmjs.com/package/nenrin-verify"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
nenrin-verify = "nenrin_verify.cli:main"
|
|
30
|
+
nenrin-agreement-verify = "nenrin_verify.cli:agreement_main"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
nenrin_verify = ["VENDORED.json", "py.typed", "selftest.json"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agreement_verify.py": {
|
|
3
|
+
"source": "workers/hs-ledger/nenrin/agreement-v0/agreement_verify.py",
|
|
4
|
+
"source_sha256": "563887cf0b5c111a37f1ad676c946ecedfe27a42058befd1e819795689f9f691",
|
|
5
|
+
"vendored_sha256": "5a143e177ee055d4e0da9c83ea5ad3901bcf9fec0a5c4cbdb7d83901247697e3"
|
|
6
|
+
},
|
|
7
|
+
"key_succession.py": {
|
|
8
|
+
"source": "workers/hs-ledger/nenrin/agreement-v0/key_succession.py",
|
|
9
|
+
"source_sha256": "0d0209b318f654d0995ac6ca40f5194064cd6e8b49459d1bbbcfdb957b61452d",
|
|
10
|
+
"vendored_sha256": "fd3601681733cc3a5a9b30a2a459f5c6e49ada7167055775283493084c48178a"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""nenrin-verify: recompute NENRIN evidence yourself, offline, in your own Python process.
|
|
2
|
+
|
|
3
|
+
The Python twin of the npm package nenrin-verify. Two verifiers, each held to its JavaScript counterpart:
|
|
4
|
+
|
|
5
|
+
provenance verify_provenance / consume_evidence: one A2A task's delegation chain, execution receipt,
|
|
6
|
+
pre-execution intent and outcome evidence (port of nenrin_verify.mjs; same report, key for key)
|
|
7
|
+
agreement agreement_verify.verify: a two-party agreement record (a2a-agreement-v1 / v1.1), the repository's
|
|
8
|
+
own Python verifier, which returns the same report as the JavaScript one on 5,286 frozen cases
|
|
9
|
+
|
|
10
|
+
No network, no clock, no score. A signature proves who asserted, not that the assertion is true; every report
|
|
11
|
+
says what it does not establish.
|
|
12
|
+
"""
|
|
13
|
+
import hashlib
|
|
14
|
+
|
|
15
|
+
from ._js import assign as _assign, stringify as _stringify, loads as js_loads
|
|
16
|
+
from .provenance import (VERIFIER_VERSION, candidate_evidence_set, consume_evidence, did_key_resolver,
|
|
17
|
+
evidence_id, grant_ref, intent_id, posture_line, preflight_report,
|
|
18
|
+
public_key_from_did_key, receipt_id, verify_provenance)
|
|
19
|
+
from . import agreement_verify
|
|
20
|
+
|
|
21
|
+
__version__ = "0.1.0"
|
|
22
|
+
|
|
23
|
+
__all__ = ["verify_provenance", "consume_evidence", "posture_line", "candidate_evidence_set", "preflight_report",
|
|
24
|
+
"public_key_from_did_key", "did_key_resolver", "evidence_id", "grant_ref", "receipt_id", "intent_id",
|
|
25
|
+
"agreement_verify", "report_sha256", "report_json", "verify_bundle", "js_loads", "VERIFIER_VERSION", "__version__"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def report_sha256(report):
|
|
29
|
+
"""sha256 of the report's sorted-key JSON, the same bytes the JavaScript side hashes for the parity check:
|
|
30
|
+
keys ordered by UTF-16 code unit at every depth, no whitespace, numbers as JavaScript writes them."""
|
|
31
|
+
return hashlib.sha256(_stringify(report, sort_keys=True).encode("utf-8")).hexdigest()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def verify_bundle(bundle):
|
|
35
|
+
"""What `nenrin-verify bundle.json` does: Object.assign({}, bundle, {resolve: didKeyResolver}), then verify,
|
|
36
|
+
offline. Any JSON value is accepted the way the JavaScript CLI accepts it (an array or null verifies as {})."""
|
|
37
|
+
return verify_provenance(_assign(bundle, {"resolve": did_key_resolver}))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def report_json(report, indent=2):
|
|
41
|
+
"""The report as JSON text, written the way JSON.stringify(report, null, indent) writes it. Use this rather
|
|
42
|
+
than json.dumps: a report can carry the port's stand-in for JavaScript undefined (a key the JavaScript leaves
|
|
43
|
+
out of its output), which json.dumps cannot write."""
|
|
44
|
+
return _stringify(report, indent=indent)
|