gpa-verify 1.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.
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .venv/
14
+ venv/
15
+ .env
16
+ .idea/
17
+ .vscode/
18
+ *.swp
19
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to `gpa-verify` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [1.0.0] — 2026-05-07
9
+
10
+ ### Added
11
+ * Initial release.
12
+ * Seven-layer offline verification:
13
+ bundle integrity, cross-references, chain integrity, eIDAS RFC3161
14
+ signature (with timeStamping EKU enforcement), anchor canonical hash,
15
+ OpenTimestamps receipt structure, TLS evidence consistency.
16
+ * Pretty CLI output with colour and JSON output for automation.
17
+ * Support for evidence bundle formats `getproofanchor-evidence-1` and
18
+ `getproofanchor-evidence-2`.
19
+ * Pure-Python — no native dependencies, no GetProofAnchor servers.
20
+
21
+ [1.0.0]: https://github.com/getproofanchor/gpa-verify/releases/tag/v1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GetProofAnchor
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,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpa-verify
3
+ Version: 1.0.0
4
+ Summary: Independent verifier for GetProofAnchor evidence bundles
5
+ Project-URL: Homepage, https://getproofanchor.com
6
+ Project-URL: Repository, https://github.com/getproofanchor/gpa-verify
7
+ Project-URL: Issues, https://github.com/getproofanchor/gpa-verify/issues
8
+ Author-email: GetProofAnchor <support@getproofanchor.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: eidas,evidence,forensic,opentimestamps,rfc3161,verification
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: Legal Industry
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Security :: Cryptography
25
+ Classifier: Topic :: System :: Archiving
26
+ Requires-Python: >=3.9
27
+ Requires-Dist: asn1crypto>=1.5
28
+ Requires-Dist: cryptography>=41.0
29
+ Description-Content-Type: text/markdown
30
+
31
+ # gpa-verify
32
+
33
+ **Independent verifier for GetProofAnchor evidence bundles.**
34
+
35
+ Reads ONLY the bundled ZIP — no network, no GetProofAnchor servers
36
+ required. Same input → same verdict on every machine.
37
+
38
+ [![CI](https://github.com/getproofanchor/gpa-verify/actions/workflows/ci.yml/badge.svg)](https://github.com/getproofanchor/gpa-verify/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/gpa-verify.svg)](https://pypi.org/project/gpa-verify/)
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
41
+
42
+ ---
43
+
44
+ ## Why does this exist?
45
+
46
+ A GetProofAnchor evidence bundle is a `.zip` containing a screenshot,
47
+ HTML, HAR, video, TLS chain, eIDAS qualified timestamp, OpenTimestamps
48
+ Bitcoin anchor, and an append-only hash chain. The bundle claims:
49
+
50
+ > *This URL existed in this exact form at this exact time.*
51
+
52
+ For court use, this claim must be **independently verifiable** — a
53
+ forensic expert appointed by a court should be able to confirm or deny
54
+ it without trusting GetProofAnchor.
55
+
56
+ `gpa-verify` is the reference implementation of that verification
57
+ protocol. It runs offline, has no GetProofAnchor dependencies at
58
+ runtime, and produces a deterministic verdict.
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install gpa-verify
64
+ ```
65
+
66
+ Requires Python 3.9+. Two pure-Python wheels are installed automatically:
67
+ `asn1crypto` (RFC3161 / CMS / X.509 parsing) and `cryptography` (RSA /
68
+ ECDSA signature verification).
69
+
70
+ ## Use
71
+
72
+ ```bash
73
+ gpa-verify path/to/GetProofAnchor_Evidence_*.zip
74
+ ```
75
+
76
+ ```
77
+ GetProofAnchor Evidence Verifier
78
+ ────────────────────────────────────────────────────────────────
79
+ Proof ID: aeb2b103-d7c5-441c-b540-c0df600a34cf
80
+ Bundle format: getproofanchor-evidence-2
81
+ Generated at: 2026-05-07T08:30:25Z
82
+
83
+ [PASS] bundle_integrity
84
+ 31/31 files OK
85
+
86
+ [PASS] cross_references
87
+ 5/5 cross-checks OK
88
+
89
+ [PASS] chain_integrity
90
+ 1 entries, all linked
91
+
92
+ [PASS] eidas_signature
93
+ valid RFC3161 token, signed by CN=SK TIMESTAMPING UNIT 2026R,
94
+ gen_time=2026-05-07T08:30:24+00:00
95
+
96
+ [PASS] anchor_canonical_hash
97
+ canonical SHA matches manifest (968f0c691384c87c...)
98
+
99
+ [PASS] ots_receipt
100
+ valid OTS proof for anchor SHA 968f0c69..., bitcoin status=pending
101
+
102
+ [PASS] tls_evidence
103
+ leaf cert SHA-256 matches tls.json (e7668f38708d0411...)
104
+
105
+ ────────────────────────────────────────────────────────────────
106
+ ✓ VERIFIED (7 passed, 0 failed, 0 skipped)
107
+ ```
108
+
109
+ ### JSON output for automation
110
+
111
+ ```bash
112
+ gpa-verify --json bundle.zip > report.json
113
+ ```
114
+
115
+ ### Exit codes
116
+
117
+ | Code | Meaning |
118
+ |------|---------|
119
+ | 0 | All checks passed |
120
+ | 1 | At least one check failed |
121
+ | 2 | Invalid arguments / cannot read ZIP |
122
+
123
+ ## What it actually checks
124
+
125
+ Verification runs in seven layers, each tightening the forensic claim.
126
+ **All layers must pass for a `VERIFIED` verdict.**
127
+
128
+ ### Layer 1 — Bundle integrity
129
+ Every file declared in `manifest.json` exists and its SHA-256 matches.
130
+ Catches any byte-level tampering with the bundle.
131
+
132
+ ### Layer 2 — Cross-references
133
+ `proof.json`'s SHA-256 claims for `screenshot.png`, `page.html`,
134
+ `content.txt`, and `capture/capture_meta.json` agree with actual file
135
+ contents. Sidecar `*.sha256` files agree. Catches selective tamper that
136
+ fixes one file but forgets another.
137
+
138
+ ### Layer 3 — Chain integrity
139
+ `chain/proof_chain.jsonl` entries form a valid hash chain:
140
+ `entry_hash == SHA256(prev_hash | event_type | proof_id | data_hash)`,
141
+ recursively verified. `chain/chain_head.json` matches the last entry.
142
+ Catches tampering with the append-only event log.
143
+
144
+ ### Layer 4 — eIDAS signature
145
+ **This is the cryptographic heart of the verification.**
146
+ The RFC3161 token in `timestamp/eidas.tsr` is verified for:
147
+
148
+ 1. **Status** — token grant status is `granted` or `granted_with_mods`.
149
+ 2. **Imprint** — the message digest the TSA signed equals
150
+ `SHA256(timestamp/eidas_payload.json)`. Binds the timestamp to *this*
151
+ evidence bundle, not someone else's.
152
+ 3. **CMS signature** — the `SignedData` blob is verified against the
153
+ signer certificate's public key, with proper signed-attributes
154
+ re-encoding (`[0]` → `SET OF` per RFC 5652).
155
+ 4. **timeStamping EKU** — the signer certificate has the
156
+ `id-kp-timeStamping` Extended Key Usage. Defends against substitution
157
+ from a non-timestamping certificate.
158
+
159
+ Together these prove the token can ONLY have been issued by the named
160
+ TSA, and ONLY for our exact payload.
161
+
162
+ ### Layer 5 — Anchor canonical hash
163
+ `manifest.anchor.payload_sha256` equals SHA-256 of the canonical JSON
164
+ of `anchor/anchor_payload.json` (sorted keys, no whitespace, UTF-8).
165
+ Catches anchor receipt pointing to a different chain than ours.
166
+
167
+ ### Layer 6 — OTS receipt structure
168
+ `anchor/anchor_receipt.ots` is a valid OpenTimestamps proof and its
169
+ file-hash field equals the anchor canonical SHA. Catches substituted
170
+ Bitcoin anchors. (Full Bitcoin block confirmation requires a Bitcoin
171
+ node and is out of scope; use the [`ots`
172
+ client](https://opentimestamps.org/) for that.)
173
+
174
+ ### Layer 7 — TLS evidence
175
+ `tls/leaf_cert.pem` SHA-256 fingerprint matches the `network/tls.json`
176
+ claim. Confirms the TLS leaf certificate stored in the bundle is the
177
+ same one observed at capture time.
178
+
179
+ ## Threat model
180
+
181
+ `gpa-verify` defends against:
182
+
183
+ | Attack | Caught by |
184
+ |--------|-----------|
185
+ | Modify any file (image, HTML, HAR, etc.) | Layer 1 |
186
+ | Modify file + manifest SHA | Layer 2 (proof.json) |
187
+ | Modify chain entry | Layer 3 |
188
+ | Forge eIDAS payload + update all SHAs | Layer 4 (imprint mismatch) |
189
+ | Substitute eIDAS token from non-TSA cert | Layer 4 (EKU check) |
190
+ | Substitute anchor for a different chain | Layer 5 |
191
+ | Substitute OTS receipt | Layer 6 |
192
+ | Substitute leaf TLS cert | Layer 7 |
193
+
194
+ It does NOT verify:
195
+
196
+ * Whether the OpenTimestamps receipt has been confirmed in a Bitcoin
197
+ block (use the `ots` client).
198
+ * Whether the TSA's certificate chain validates against the EU Trusted
199
+ List in real time (the bundle includes a snapshot of the EU Trusted
200
+ List as supporting context, but online validation is out of scope).
201
+ * Whether the captured page actually showed what the screenshot shows
202
+ (this is a content question, not a cryptographic one — open the
203
+ bundled `capture.webm` video for visual confirmation).
204
+
205
+ ## API
206
+
207
+ ```python
208
+ from gpa_verify import verify_evidence_zip
209
+
210
+ with open("evidence.zip", "rb") as f:
211
+ report = verify_evidence_zip(f.read())
212
+
213
+ if report.all_passed:
214
+ print(f"VERIFIED proof {report.proof_id}")
215
+ else:
216
+ for c in report.checks:
217
+ if not c.passed and not c.skipped:
218
+ print(f"FAIL {c.name}: {c.detail}")
219
+ ```
220
+
221
+ ## Building from source
222
+
223
+ ```bash
224
+ git clone https://github.com/getproofanchor/gpa-verify.git
225
+ cd gpa-verify
226
+ pip install -e .[dev]
227
+ pytest
228
+ ```
229
+
230
+ ## License
231
+
232
+ MIT — see [LICENSE](LICENSE).
233
+
234
+ This tool is published as open source so any forensic expert,
235
+ court-appointed examiner, or independent journalist can audit the
236
+ verification logic line-by-line. The cryptographic algorithms used
237
+ (RFC 3161, CMS / RFC 5652, X.509, SHA-256, RSA, ECDSA, OpenTimestamps)
238
+ are open standards.
239
+
240
+ ## Standards & references
241
+
242
+ * [eIDAS Regulation (EU) 910/2014](https://eur-lex.europa.eu/eli/reg/2014/910/oj),
243
+ Art. 41 — qualified electronic timestamps
244
+ * [RFC 3161](https://datatracker.ietf.org/doc/html/rfc3161) — Time-Stamp Protocol
245
+ * [RFC 5652](https://datatracker.ietf.org/doc/html/rfc5652) — Cryptographic Message Syntax
246
+ * [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) — X.509 certificates
247
+ * [ISO/IEC 27037:2012](https://www.iso.org/standard/44381.html) — digital evidence guidelines
248
+ * [OpenTimestamps](https://opentimestamps.org/) — Bitcoin-anchored timestamps
249
+ * [EU Trusted List](https://ec.europa.eu/tools/lotl/eu-lotl.xml) — qualified TSPs
@@ -0,0 +1,219 @@
1
+ # gpa-verify
2
+
3
+ **Independent verifier for GetProofAnchor evidence bundles.**
4
+
5
+ Reads ONLY the bundled ZIP — no network, no GetProofAnchor servers
6
+ required. Same input → same verdict on every machine.
7
+
8
+ [![CI](https://github.com/getproofanchor/gpa-verify/actions/workflows/ci.yml/badge.svg)](https://github.com/getproofanchor/gpa-verify/actions/workflows/ci.yml)
9
+ [![PyPI](https://img.shields.io/pypi/v/gpa-verify.svg)](https://pypi.org/project/gpa-verify/)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
11
+
12
+ ---
13
+
14
+ ## Why does this exist?
15
+
16
+ A GetProofAnchor evidence bundle is a `.zip` containing a screenshot,
17
+ HTML, HAR, video, TLS chain, eIDAS qualified timestamp, OpenTimestamps
18
+ Bitcoin anchor, and an append-only hash chain. The bundle claims:
19
+
20
+ > *This URL existed in this exact form at this exact time.*
21
+
22
+ For court use, this claim must be **independently verifiable** — a
23
+ forensic expert appointed by a court should be able to confirm or deny
24
+ it without trusting GetProofAnchor.
25
+
26
+ `gpa-verify` is the reference implementation of that verification
27
+ protocol. It runs offline, has no GetProofAnchor dependencies at
28
+ runtime, and produces a deterministic verdict.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install gpa-verify
34
+ ```
35
+
36
+ Requires Python 3.9+. Two pure-Python wheels are installed automatically:
37
+ `asn1crypto` (RFC3161 / CMS / X.509 parsing) and `cryptography` (RSA /
38
+ ECDSA signature verification).
39
+
40
+ ## Use
41
+
42
+ ```bash
43
+ gpa-verify path/to/GetProofAnchor_Evidence_*.zip
44
+ ```
45
+
46
+ ```
47
+ GetProofAnchor Evidence Verifier
48
+ ────────────────────────────────────────────────────────────────
49
+ Proof ID: aeb2b103-d7c5-441c-b540-c0df600a34cf
50
+ Bundle format: getproofanchor-evidence-2
51
+ Generated at: 2026-05-07T08:30:25Z
52
+
53
+ [PASS] bundle_integrity
54
+ 31/31 files OK
55
+
56
+ [PASS] cross_references
57
+ 5/5 cross-checks OK
58
+
59
+ [PASS] chain_integrity
60
+ 1 entries, all linked
61
+
62
+ [PASS] eidas_signature
63
+ valid RFC3161 token, signed by CN=SK TIMESTAMPING UNIT 2026R,
64
+ gen_time=2026-05-07T08:30:24+00:00
65
+
66
+ [PASS] anchor_canonical_hash
67
+ canonical SHA matches manifest (968f0c691384c87c...)
68
+
69
+ [PASS] ots_receipt
70
+ valid OTS proof for anchor SHA 968f0c69..., bitcoin status=pending
71
+
72
+ [PASS] tls_evidence
73
+ leaf cert SHA-256 matches tls.json (e7668f38708d0411...)
74
+
75
+ ────────────────────────────────────────────────────────────────
76
+ ✓ VERIFIED (7 passed, 0 failed, 0 skipped)
77
+ ```
78
+
79
+ ### JSON output for automation
80
+
81
+ ```bash
82
+ gpa-verify --json bundle.zip > report.json
83
+ ```
84
+
85
+ ### Exit codes
86
+
87
+ | Code | Meaning |
88
+ |------|---------|
89
+ | 0 | All checks passed |
90
+ | 1 | At least one check failed |
91
+ | 2 | Invalid arguments / cannot read ZIP |
92
+
93
+ ## What it actually checks
94
+
95
+ Verification runs in seven layers, each tightening the forensic claim.
96
+ **All layers must pass for a `VERIFIED` verdict.**
97
+
98
+ ### Layer 1 — Bundle integrity
99
+ Every file declared in `manifest.json` exists and its SHA-256 matches.
100
+ Catches any byte-level tampering with the bundle.
101
+
102
+ ### Layer 2 — Cross-references
103
+ `proof.json`'s SHA-256 claims for `screenshot.png`, `page.html`,
104
+ `content.txt`, and `capture/capture_meta.json` agree with actual file
105
+ contents. Sidecar `*.sha256` files agree. Catches selective tamper that
106
+ fixes one file but forgets another.
107
+
108
+ ### Layer 3 — Chain integrity
109
+ `chain/proof_chain.jsonl` entries form a valid hash chain:
110
+ `entry_hash == SHA256(prev_hash | event_type | proof_id | data_hash)`,
111
+ recursively verified. `chain/chain_head.json` matches the last entry.
112
+ Catches tampering with the append-only event log.
113
+
114
+ ### Layer 4 — eIDAS signature
115
+ **This is the cryptographic heart of the verification.**
116
+ The RFC3161 token in `timestamp/eidas.tsr` is verified for:
117
+
118
+ 1. **Status** — token grant status is `granted` or `granted_with_mods`.
119
+ 2. **Imprint** — the message digest the TSA signed equals
120
+ `SHA256(timestamp/eidas_payload.json)`. Binds the timestamp to *this*
121
+ evidence bundle, not someone else's.
122
+ 3. **CMS signature** — the `SignedData` blob is verified against the
123
+ signer certificate's public key, with proper signed-attributes
124
+ re-encoding (`[0]` → `SET OF` per RFC 5652).
125
+ 4. **timeStamping EKU** — the signer certificate has the
126
+ `id-kp-timeStamping` Extended Key Usage. Defends against substitution
127
+ from a non-timestamping certificate.
128
+
129
+ Together these prove the token can ONLY have been issued by the named
130
+ TSA, and ONLY for our exact payload.
131
+
132
+ ### Layer 5 — Anchor canonical hash
133
+ `manifest.anchor.payload_sha256` equals SHA-256 of the canonical JSON
134
+ of `anchor/anchor_payload.json` (sorted keys, no whitespace, UTF-8).
135
+ Catches anchor receipt pointing to a different chain than ours.
136
+
137
+ ### Layer 6 — OTS receipt structure
138
+ `anchor/anchor_receipt.ots` is a valid OpenTimestamps proof and its
139
+ file-hash field equals the anchor canonical SHA. Catches substituted
140
+ Bitcoin anchors. (Full Bitcoin block confirmation requires a Bitcoin
141
+ node and is out of scope; use the [`ots`
142
+ client](https://opentimestamps.org/) for that.)
143
+
144
+ ### Layer 7 — TLS evidence
145
+ `tls/leaf_cert.pem` SHA-256 fingerprint matches the `network/tls.json`
146
+ claim. Confirms the TLS leaf certificate stored in the bundle is the
147
+ same one observed at capture time.
148
+
149
+ ## Threat model
150
+
151
+ `gpa-verify` defends against:
152
+
153
+ | Attack | Caught by |
154
+ |--------|-----------|
155
+ | Modify any file (image, HTML, HAR, etc.) | Layer 1 |
156
+ | Modify file + manifest SHA | Layer 2 (proof.json) |
157
+ | Modify chain entry | Layer 3 |
158
+ | Forge eIDAS payload + update all SHAs | Layer 4 (imprint mismatch) |
159
+ | Substitute eIDAS token from non-TSA cert | Layer 4 (EKU check) |
160
+ | Substitute anchor for a different chain | Layer 5 |
161
+ | Substitute OTS receipt | Layer 6 |
162
+ | Substitute leaf TLS cert | Layer 7 |
163
+
164
+ It does NOT verify:
165
+
166
+ * Whether the OpenTimestamps receipt has been confirmed in a Bitcoin
167
+ block (use the `ots` client).
168
+ * Whether the TSA's certificate chain validates against the EU Trusted
169
+ List in real time (the bundle includes a snapshot of the EU Trusted
170
+ List as supporting context, but online validation is out of scope).
171
+ * Whether the captured page actually showed what the screenshot shows
172
+ (this is a content question, not a cryptographic one — open the
173
+ bundled `capture.webm` video for visual confirmation).
174
+
175
+ ## API
176
+
177
+ ```python
178
+ from gpa_verify import verify_evidence_zip
179
+
180
+ with open("evidence.zip", "rb") as f:
181
+ report = verify_evidence_zip(f.read())
182
+
183
+ if report.all_passed:
184
+ print(f"VERIFIED proof {report.proof_id}")
185
+ else:
186
+ for c in report.checks:
187
+ if not c.passed and not c.skipped:
188
+ print(f"FAIL {c.name}: {c.detail}")
189
+ ```
190
+
191
+ ## Building from source
192
+
193
+ ```bash
194
+ git clone https://github.com/getproofanchor/gpa-verify.git
195
+ cd gpa-verify
196
+ pip install -e .[dev]
197
+ pytest
198
+ ```
199
+
200
+ ## License
201
+
202
+ MIT — see [LICENSE](LICENSE).
203
+
204
+ This tool is published as open source so any forensic expert,
205
+ court-appointed examiner, or independent journalist can audit the
206
+ verification logic line-by-line. The cryptographic algorithms used
207
+ (RFC 3161, CMS / RFC 5652, X.509, SHA-256, RSA, ECDSA, OpenTimestamps)
208
+ are open standards.
209
+
210
+ ## Standards & references
211
+
212
+ * [eIDAS Regulation (EU) 910/2014](https://eur-lex.europa.eu/eli/reg/2014/910/oj),
213
+ Art. 41 — qualified electronic timestamps
214
+ * [RFC 3161](https://datatracker.ietf.org/doc/html/rfc3161) — Time-Stamp Protocol
215
+ * [RFC 5652](https://datatracker.ietf.org/doc/html/rfc5652) — Cryptographic Message Syntax
216
+ * [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) — X.509 certificates
217
+ * [ISO/IEC 27037:2012](https://www.iso.org/standard/44381.html) — digital evidence guidelines
218
+ * [OpenTimestamps](https://opentimestamps.org/) — Bitcoin-anchored timestamps
219
+ * [EU Trusted List](https://ec.europa.eu/tools/lotl/eu-lotl.xml) — qualified TSPs
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.18"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "gpa-verify"
7
+ version = "1.0.0"
8
+ description = "Independent verifier for GetProofAnchor evidence bundles"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "GetProofAnchor", email = "support@getproofanchor.com" },
14
+ ]
15
+ keywords = ["forensic", "evidence", "rfc3161", "eidas", "opentimestamps", "verification"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Intended Audience :: Legal Industry",
19
+ "Intended Audience :: Information Technology",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
+ "Topic :: Security :: Cryptography",
30
+ "Topic :: System :: Archiving",
31
+ ]
32
+ dependencies = [
33
+ "asn1crypto>=1.5",
34
+ "cryptography>=41.0",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://getproofanchor.com"
39
+ Repository = "https://github.com/getproofanchor/gpa-verify"
40
+ Issues = "https://github.com/getproofanchor/gpa-verify/issues"
41
+
42
+ [project.scripts]
43
+ gpa-verify = "gpa_verify.cli:main"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/gpa_verify"]
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = [
50
+ "/src",
51
+ "/tests",
52
+ "/README.md",
53
+ "/LICENSE",
54
+ "/CHANGELOG.md",
55
+ ]
@@ -0,0 +1,23 @@
1
+ """
2
+ gpa-verify — independent verifier for GetProofAnchor evidence bundles.
3
+
4
+ This package is the reference implementation of the verification protocol
5
+ documented at https://getproofanchor.com/verify. It does NOT depend on
6
+ any GetProofAnchor server — given an evidence ZIP and the bundled
7
+ artifacts, it produces a deterministic verdict.
8
+ """
9
+
10
+ __version__ = "1.0.0"
11
+
12
+ from .core import (
13
+ CheckResult,
14
+ VerifyReport,
15
+ verify_evidence_zip,
16
+ )
17
+
18
+ __all__ = [
19
+ "CheckResult",
20
+ "VerifyReport",
21
+ "verify_evidence_zip",
22
+ "__version__",
23
+ ]