sia-verifier 1.3.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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SIA Sentinel contributors
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,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: sia-verifier
3
+ Version: 1.3.0
4
+ Summary: Independent verifier for sia-attestation/1 Proof-of-Savings attestations — no trust in the auditor required
5
+ Author: SIA Sentinel contributors
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 SIA Sentinel contributors
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/sia-sentinel/sia-sentinel
29
+ Keywords: ai,finops,attestation,ed25519,verification
30
+ Classifier: Development Status :: 5 - Production/Stable
31
+ Classifier: Intended Audience :: Developers
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Topic :: Security :: Cryptography
35
+ Requires-Python: >=3.9
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: cryptography>=41.0
39
+ Dynamic: license-file
40
+
41
+ # sia-verifier
42
+
43
+ **Independent verifier for `sia-attestation/1` Proof-of-Savings attestations.**
44
+
45
+ This package lets *anyone* verify an AI cost-savings attestation **without
46
+ trusting the auditor**. It needs only the attestation document and the
47
+ issuer's public key (carried inside the document). No network calls, no
48
+ Sentinel server, no SDK.
49
+
50
+ ## Why this exists
51
+
52
+ A Proof-of-Savings attestation claims: *"this AI workload was audited, the
53
+ savings are real, and here is cryptographic proof."* The whole point of the
54
+ claim is that you should not have to take the auditor's word for it. This
55
+ verifier is the reference implementation of that promise:
56
+
57
+ - **Ed25519 receipt signature** — the receipt (including `receipt_id` and the
58
+ reproducibility manifest) is signed; any tampering breaks the signature.
59
+ - **Claim consistency** — the public claim must match the signed
60
+ `safety_approved` field; a forged claim fails verification.
61
+ - **TrustChain hash chain** (optional) — given a ledger export, every entry is
62
+ recomputed; removal, insertion, reorder or content tampering breaks it.
63
+ - **Checkpoints** (optional) — signed commitments pinning the chain head.
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ pip install sia-verifier
69
+ ```
70
+
71
+ Single dependency: `cryptography`. Python 3.9+.
72
+
73
+ ## CLI
74
+
75
+ ```bash
76
+ # Verify an attestation document (the JSON served at GET /v1/attestations/{id})
77
+ sia-verifier attestation.json
78
+
79
+ # Also verify the hash chain from a ledger export
80
+ sia-verifier attestation.json --chain registry.jsonl
81
+
82
+ # Also verify a checkpoint signature
83
+ sia-verifier attestation.json --checkpoint checkpoint.json
84
+
85
+ # Machine-readable verdict
86
+ sia-verifier attestation.json --json
87
+ ```
88
+
89
+ Exit code `0` = valid, `1` = invalid, `2` = input error — safe to wire into CI.
90
+
91
+ ## Python API
92
+
93
+ ```python
94
+ import json
95
+ from sia_verifier import verify_attestation, verify_chain
96
+
97
+ attestation = json.load(open("attestation.json"))
98
+ verdict = verify_attestation(attestation)
99
+
100
+ assert verdict.valid, verdict.reasons
101
+ assert verdict.receipt_signature_valid
102
+ assert verdict.claim_consistent
103
+ ```
104
+
105
+ ## What is NOT verified (by design)
106
+
107
+ - The `verification.*` fields inside the attestation are the *server's* live
108
+ opinion and are deliberately ignored — they are not part of the signed
109
+ commitment.
110
+ - The issuer's public key is taken from the document itself. For a
111
+ higher-assurance check, pin the issuer's key out-of-band (e.g. from the
112
+ issuer's published key registry) and compare it to `issuer.public_key`
113
+ before trusting the verdict.
114
+ - This verifier checks cryptographic integrity, not whether the audit
115
+ methodology was sound. Methodology is documented in the attestation's
116
+ reproducibility manifest.
117
+
118
+ ## Specification
119
+
120
+ The full format is defined by the `sia-attestation/1` specification
121
+ (commitment construction, hash chain, checkpoints). See the SIA Sentinel
122
+ repository, `docs/attestation-spec.md`.
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,86 @@
1
+ # sia-verifier
2
+
3
+ **Independent verifier for `sia-attestation/1` Proof-of-Savings attestations.**
4
+
5
+ This package lets *anyone* verify an AI cost-savings attestation **without
6
+ trusting the auditor**. It needs only the attestation document and the
7
+ issuer's public key (carried inside the document). No network calls, no
8
+ Sentinel server, no SDK.
9
+
10
+ ## Why this exists
11
+
12
+ A Proof-of-Savings attestation claims: *"this AI workload was audited, the
13
+ savings are real, and here is cryptographic proof."* The whole point of the
14
+ claim is that you should not have to take the auditor's word for it. This
15
+ verifier is the reference implementation of that promise:
16
+
17
+ - **Ed25519 receipt signature** — the receipt (including `receipt_id` and the
18
+ reproducibility manifest) is signed; any tampering breaks the signature.
19
+ - **Claim consistency** — the public claim must match the signed
20
+ `safety_approved` field; a forged claim fails verification.
21
+ - **TrustChain hash chain** (optional) — given a ledger export, every entry is
22
+ recomputed; removal, insertion, reorder or content tampering breaks it.
23
+ - **Checkpoints** (optional) — signed commitments pinning the chain head.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install sia-verifier
29
+ ```
30
+
31
+ Single dependency: `cryptography`. Python 3.9+.
32
+
33
+ ## CLI
34
+
35
+ ```bash
36
+ # Verify an attestation document (the JSON served at GET /v1/attestations/{id})
37
+ sia-verifier attestation.json
38
+
39
+ # Also verify the hash chain from a ledger export
40
+ sia-verifier attestation.json --chain registry.jsonl
41
+
42
+ # Also verify a checkpoint signature
43
+ sia-verifier attestation.json --checkpoint checkpoint.json
44
+
45
+ # Machine-readable verdict
46
+ sia-verifier attestation.json --json
47
+ ```
48
+
49
+ Exit code `0` = valid, `1` = invalid, `2` = input error — safe to wire into CI.
50
+
51
+ ## Python API
52
+
53
+ ```python
54
+ import json
55
+ from sia_verifier import verify_attestation, verify_chain
56
+
57
+ attestation = json.load(open("attestation.json"))
58
+ verdict = verify_attestation(attestation)
59
+
60
+ assert verdict.valid, verdict.reasons
61
+ assert verdict.receipt_signature_valid
62
+ assert verdict.claim_consistent
63
+ ```
64
+
65
+ ## What is NOT verified (by design)
66
+
67
+ - The `verification.*` fields inside the attestation are the *server's* live
68
+ opinion and are deliberately ignored — they are not part of the signed
69
+ commitment.
70
+ - The issuer's public key is taken from the document itself. For a
71
+ higher-assurance check, pin the issuer's key out-of-band (e.g. from the
72
+ issuer's published key registry) and compare it to `issuer.public_key`
73
+ before trusting the verdict.
74
+ - This verifier checks cryptographic integrity, not whether the audit
75
+ methodology was sound. Methodology is documented in the attestation's
76
+ reproducibility manifest.
77
+
78
+ ## Specification
79
+
80
+ The full format is defined by the `sia-attestation/1` specification
81
+ (commitment construction, hash chain, checkpoints). See the SIA Sentinel
82
+ repository, `docs/attestation-spec.md`.
83
+
84
+ ## License
85
+
86
+ MIT
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sia-verifier"
7
+ version = "1.3.0"
8
+ description = "Independent verifier for sia-attestation/1 Proof-of-Savings attestations — no trust in the auditor required"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "SIA Sentinel contributors" }]
13
+ keywords = ["ai", "finops", "attestation", "ed25519", "verification"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Security :: Cryptography",
20
+ ]
21
+ # Единственная зависимость — криптография. Никаких сетевых вызовов,
22
+ # никакого SDK Sentinel: верификатор должен работать у любой третьей
23
+ # стороны, которая не доверяет аудитору. (rederive делает один
24
+ # опциональный живой запрос к публичному логу Rekor — по флагу.)
25
+ dependencies = ["cryptography>=41.0"]
26
+
27
+ [project.scripts]
28
+ sia-verifier = "sia_verifier.__main__:main"
29
+ sia-rederive = "sia_verifier.rederive:main"
30
+ sia-holdout = "sia_verifier.holdout:main"
31
+ sia-replay = "sia_verifier.replay:main"
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/sia-sentinel/sia-sentinel"
35
+
36
+ [tool.setuptools.packages.find]
37
+ include = ["sia_verifier*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,77 @@
1
+ """sia-verifier — независимый верификатор аттестаций Proof-of-Savings.
2
+
3
+ Минимальный пакет (только `cryptography` + stdlib), который проверяет
4
+ аттестации стандарта ``sia-attestation/1`` **без доверия к аудитору**:
5
+ нужен только сам документ аттестации и (опционально) выгрузка журнала
6
+ TrustChain. Никаких сетевых вызовов, никакого SDK Sentinel.
7
+
8
+ Что проверяется:
9
+
10
+ 1. **Подпись квитанции** — Ed25519 (RFC 8032) над каноническим JSON
11
+ коммитмента, восстановленного из полей квитанции (включая
12
+ ``receipt_id``). Публичный ключ берётся из ``issuer.public_key``
13
+ аттестации и принимается только в виде base64 raw 32 байт.
14
+ 2. **Согласованность заявления** — ``receipt.safety_approved`` должен
15
+ совпадать с ``claim.savings_verified``: подпись покрывает именно
16
+ ``safety_approved``, поэтому расхождение означает подделку заявления.
17
+ 3. **Хеш-цепочка TrustChain** (опционально, по выгрузке журнала) —
18
+ каждая запись пересчитывается из ``seq``, ``prev_hash`` и содержимого;
19
+ удаление, вставка, перестановка или подмена записи ломают цепочку.
20
+ 4. **Чекпоинты** — подписанные коммитменты на голову цепочки (v1) и на
21
+ голову цепочки + Merkle tree head (v2).
22
+ 5. **Merkle-доказательства** (RFC 6962) — ``verify_inclusion`` проверяет
23
+ включение записи в tree head, ``verify_consistency`` — что дерево
24
+ размера N является продолжением дерева размера M.
25
+
26
+ CLI (v1.3.0)::
27
+
28
+ sia-verifier attestation.json [--chain registry.jsonl] [--checkpoint cp.json]
29
+ sia-rederive [--artifacts DIR] [--flow F] [--chain C] [--no-rekor]
30
+ sia-replay --flow F --record report1.json --replay report2.json
31
+ sia-holdout make|reveal|verify ...
32
+
33
+ Первая команда проверяет подпись/цепь/чекпоинт; вторая перевыводит сам
34
+ вердикт записи (формулы считаются локально, входы пришиты к подписи через
35
+ receipt.code_hash); третья исполняет задекларированный допуск
36
+ replay_tolerance — сравнение записи с независимым повторным прогоном
37
+ (Б2: односторонний допуск «к заявлению»); четвёртая — инструмент
38
+ внешнего аудитора holdout (п.8).
39
+ Все три доступны и как ``python -m sia_verifier.<module>``.
40
+
41
+ Программа и API возвращают вердикт; ненулевой код выхода при
42
+ невалидной аттестации позволяет встраивать проверку в CI.
43
+ """
44
+ from __future__ import annotations
45
+
46
+ from .core import (
47
+ ATTESTATION_SPEC,
48
+ AttestationVerdict,
49
+ ChainVerdict,
50
+ leaf_hash,
51
+ resolve_receipt_key,
52
+ verify_attestation,
53
+ verify_chain,
54
+ verify_checkpoint,
55
+ verify_consistency,
56
+ verify_inclusion,
57
+ verify_key_declarations,
58
+ verify_receipt,
59
+ )
60
+
61
+ __version__ = "1.3.0"
62
+
63
+ __all__ = [
64
+ "ATTESTATION_SPEC",
65
+ "AttestationVerdict",
66
+ "ChainVerdict",
67
+ "leaf_hash",
68
+ "resolve_receipt_key",
69
+ "verify_attestation",
70
+ "verify_chain",
71
+ "verify_checkpoint",
72
+ "verify_consistency",
73
+ "verify_inclusion",
74
+ "verify_key_declarations",
75
+ "verify_receipt",
76
+ "__version__",
77
+ ]
@@ -0,0 +1,136 @@
1
+ """CLI независимого верификатора аттестаций Proof-of-Savings.
2
+
3
+ Примеры::
4
+
5
+ # Проверить аттестацию из файла (документ GET /v1/attestations/{id})
6
+ python -m sia_verifier attestation.json
7
+
8
+ # Проверить аттестацию + хеш-цепочку журнала
9
+ python -m sia_verifier attestation.json --chain registry.jsonl
10
+
11
+ # Проверить подпись чекпоинта
12
+ python -m sia_verifier attestation.json --checkpoint checkpoint.json
13
+
14
+ Код выхода: 0 — аттестация валидна, 1 — невалидна, 2 — ошибка ввода.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from .core import verify_attestation, verify_chain, verify_checkpoint
25
+
26
+
27
+ def _load_json(path: Path) -> Any:
28
+ return json.loads(path.read_text(encoding="utf-8"))
29
+
30
+
31
+ def _load_jsonl(path: Path) -> list[dict[str, Any]]:
32
+ entries = []
33
+ for line in path.read_text(encoding="utf-8").splitlines():
34
+ line = line.strip()
35
+ if line:
36
+ entries.append(json.loads(line))
37
+ return entries
38
+
39
+
40
+ def main(argv: list[str] | None = None) -> int:
41
+ parser = argparse.ArgumentParser(
42
+ prog="sia-verifier",
43
+ description="Independent verifier for sia-attestation/1 Proof-of-Savings attestations.",
44
+ )
45
+ parser.add_argument(
46
+ "attestation",
47
+ type=Path,
48
+ help="Path to the attestation document JSON (GET /v1/attestations/{id})",
49
+ )
50
+ parser.add_argument(
51
+ "--chain",
52
+ type=Path,
53
+ default=None,
54
+ help="Optional TrustChain ledger export (registry.jsonl) to verify the hash chain",
55
+ )
56
+ parser.add_argument(
57
+ "--checkpoint",
58
+ type=Path,
59
+ default=None,
60
+ help="Optional checkpoint JSON to verify against the issuer public key",
61
+ )
62
+ parser.add_argument(
63
+ "--json",
64
+ action="store_true",
65
+ help="Print the verdict as JSON instead of human-readable text",
66
+ )
67
+
68
+ args = parser.parse_args(argv)
69
+
70
+ try:
71
+ attestation = _load_json(args.attestation)
72
+ except (OSError, json.JSONDecodeError) as exc:
73
+ print(f"error: cannot read attestation file: {exc}", file=sys.stderr)
74
+ return 2
75
+
76
+ verdict = verify_attestation(attestation)
77
+ result: dict[str, Any] = {"attestation": verdict.to_dict()}
78
+
79
+ issuer_key = (attestation.get("issuer") or {}).get("public_key", "")
80
+ attestation_id = attestation.get("attestation_id")
81
+
82
+ if args.chain is not None:
83
+ try:
84
+ entries = _load_jsonl(args.chain)
85
+ except (OSError, json.JSONDecodeError) as exc:
86
+ print(f"error: cannot read chain file: {exc}", file=sys.stderr)
87
+ return 2
88
+
89
+ chain_verdict = verify_chain(entries, attestation_id=attestation_id)
90
+ result["chain"] = chain_verdict.to_dict()
91
+
92
+ if args.checkpoint is not None:
93
+ try:
94
+ checkpoint = _load_json(args.checkpoint)
95
+ except (OSError, json.JSONDecodeError) as exc:
96
+ print(f"error: cannot read checkpoint file: {exc}", file=sys.stderr)
97
+ return 2
98
+
99
+ result["checkpoint_valid"] = verify_checkpoint(checkpoint, issuer_key)
100
+
101
+ overall = verdict.valid
102
+ if "chain" in result:
103
+ overall = overall and result["chain"]["valid"]
104
+ if result["chain"].get("contains_attestation") is False:
105
+ overall = False
106
+ result["chain"]["reason"] = (
107
+ result["chain"].get("reason")
108
+ or "attestation_id not found in the provided chain"
109
+ )
110
+ if "checkpoint_valid" in result:
111
+ overall = overall and result["checkpoint_valid"]
112
+
113
+ if args.json:
114
+ result["valid"] = overall
115
+ print(json.dumps(result, indent=2))
116
+ else:
117
+ print(f"spec: {attestation.get('spec')}")
118
+ print(f"attestation_id: {attestation_id}")
119
+ print(f"receipt signature: {'VALID' if verdict.receipt_signature_valid else 'INVALID'}")
120
+ print(f"claim consistent: {'yes' if verdict.claim_consistent else 'NO'}")
121
+ if "chain" in result:
122
+ chain = result["chain"]
123
+ print(f"chain: {'VALID' if chain['valid'] else 'INVALID'} ({chain['entries']} entries)")
124
+ if chain.get("contains_attestation") is False:
125
+ print("chain membership: attestation NOT FOUND in chain")
126
+ if "checkpoint_valid" in result:
127
+ print(f"checkpoint signature: {'VALID' if result['checkpoint_valid'] else 'INVALID'}")
128
+ for reason in verdict.reasons:
129
+ print(f" - {reason}")
130
+ print(f"VERDICT: {'VALID' if overall else 'INVALID'}")
131
+
132
+ return 0 if overall else 1
133
+
134
+
135
+ if __name__ == "__main__":
136
+ raise SystemExit(main())