#!/usr/bin/env python3
"""Verify a Store download before extraction. Requires Python3.11+ and cryptography.
Copyright2026 ProgreTech LLC. This verification helper may be copied and modified.
Obtain the trusted public key and its SHA-256 through an independently trusted channel.
"""
import argparse
import base64
import hashlib
import json
from pathlib import Path
import re
import sys

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey


def unique_object(items):
    result = {}
    for name, value in items:
        if name in result:
            raise ValueError('Duplicate JSON field')
        result[name] = value
    return result


def reject_constant(value):
    raise ValueError("Invalid JSON number")


def verify(key_path, expected_key_hash, metadata_path, archive_path):
    if not re.fullmatch(r'[a-f0-9]{64}', expected_key_hash):
        raise ValueError('Supply the independently trusted public-key SHA-256')
    if key_path.stat().st_size > 4096 or metadata_path.stat().st_size > 32000:
        raise ValueError('Verification input exceeds its limit')
    key_bytes = key_path.read_bytes()
    if hashlib.sha256(key_bytes).hexdigest() != expected_key_hash:
        raise ValueError('Public key does not match the trusted fingerprint')
    key = serialization.load_pem_public_key(key_bytes)
    if not isinstance(key, Ed25519PublicKey):
        raise ValueError('Ed25519 public key required')
    envelope = json.loads(metadata_path.read_text(), object_pairs_hook=unique_object,
                          parse_constant=reject_constant)
    manifest = envelope['manifest']
    canonical = json.dumps(manifest, sort_keys=True, separators=(',', ':'),
                           ensure_ascii=True, allow_nan=False).encode()
    key.verify(base64.b64decode(envelope['signature'], validate=True), canonical)
    if manifest.get('format') != 'progretech-store/release-v1':
        raise ValueError('Unsupported release format')
    if not re.fullmatch(r'[a-z0-9][a-z0-9-]{1,80}', manifest.get('sku', '')):
        raise ValueError('Invalid signed product identifier')
    size = manifest.get('artifact_size')
    if type(size) is not int or not 1 <= size <= 64 * 1024 * 1024:
        raise ValueError('Invalid signed archive size')
    digest = hashlib.sha256()
    count = 0
    with archive_path.open('rb') as archive:
        while chunk := archive.read(1024 * 1024):
            count += len(chunk)
            if count > size:
                raise ValueError('Archive exceeds the signed size')
            digest.update(chunk)
    if count != size or digest.hexdigest() != manifest.get('artifact_sha256'):
        raise ValueError('Archive does not match its signed digest and size')
    return manifest


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--trusted-key', type=Path, required=True)
    parser.add_argument('--key-sha256', required=True)
    parser.add_argument('--manifest', type=Path, required=True)
    parser.add_argument('--archive', type=Path, required=True)
    args = parser.parse_args()
    try:
        manifest = verify(args.trusted_key, args.key_sha256, args.manifest, args.archive)
    except Exception:
        print('Verification failed. Do not extract or run this archive.', file=sys.stderr)
        return 1
    print('Verified signed archive:', manifest['sku'], manifest['version'])
    print('SHA-256:', manifest['artifact_sha256'])
    print('This verifies the signed bytes; review the license and deployment limits before use.')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
