32 lines
1.9 KiB
Python
32 lines
1.9 KiB
Python
"""Read-only verification of a trusted, source-bound assessment receipt; no services."""
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
def verify(repository, receipt_path, expected_sha256):
|
|
repository = Path(repository).resolve()
|
|
raw = Path(receipt_path).read_bytes()
|
|
if len(raw) > 8 * 1024 * 1024 or hashlib.sha256(raw).hexdigest() != expected_sha256:
|
|
raise ValueError('Assessment receipt differs from the protected manifest pin')
|
|
receipt = json.loads(raw)
|
|
if (receipt.get('schemaVersion') != 1 or receipt.get('scope') != 'P10-00-ASSESSMENT-ONLY'
|
|
or receipt.get('integrity') != 'PASS' or not receipt.get('files')
|
|
or not receipt.get('nativeReports') or not receipt.get('retainedDecisions')):
|
|
raise ValueError('Incomplete assessment qualification receipt')
|
|
for name, expected in receipt['files'].items():
|
|
path = (repository / name).resolve()
|
|
if not path.is_relative_to(repository) or not path.is_file():
|
|
raise ValueError('Frozen assessment source missing or outside repository: ' + name)
|
|
if hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
|
raise ValueError('Frozen assessment source changed; fresh affected qualification required: ' + name)
|
|
evidence_root = Path(receipt_path).resolve().parent
|
|
for name, expected in receipt.get('evidenceFiles', {}).items():
|
|
path = (evidence_root / name).resolve()
|
|
if not path.is_relative_to(evidence_root) or not path.is_file() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
|
raise ValueError('Accepted handoff evidence changed or missing: ' + name)
|
|
return dict(kind='REUSED_SOURCE_BOUND_EVIDENCE', receiptSha256=expected_sha256,
|
|
nativeReports=receipt['nativeReports'],
|
|
retainedDecisions=receipt['retainedDecisions'],
|
|
limits='P10-00 assessment only. No fresh product runs or implementation qualification.')
|