#!/usr/bin/env python3
"""Verify the integrity of verbum-corpus.bin signed with Ed25519.

Usage:
    python3 verify.py verbum-corpus.bin corpus-signing.pub

Exit code 0 = signature valid, 1 = invalid or error.

The container format is:
    magic(4) || version(4) || body_len(8) || body[body_len] || signature(64)

The signature covers magic+version+body_len+body. Implementation matches
crates/verbum-audit/src/lib.rs in the Verbum Rust source.

Dependency: pip install cryptography
"""

import hashlib
import struct
import sys
from pathlib import Path

try:
    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
except ImportError:
    print("Erreur : module 'cryptography' manquant.", file=sys.stderr)
    print("  Installation : pip install cryptography", file=sys.stderr)
    sys.exit(2)


MAGIC = b"VRBM"
VERSION = 1


def verify(corpus_path: Path, key_path: Path) -> int:
    if not corpus_path.is_file():
        print(f"Erreur : {corpus_path} introuvable.", file=sys.stderr)
        return 1
    if not key_path.is_file():
        print(f"Erreur : {key_path} introuvable.", file=sys.stderr)
        return 1

    pub_bytes = key_path.read_bytes()
    if len(pub_bytes) != 32:
        print(f"Erreur : clé publique attendue = 32 octets, trouvé = {len(pub_bytes)}.", file=sys.stderr)
        return 1
    pub_key = Ed25519PublicKey.from_public_bytes(pub_bytes)

    data = corpus_path.read_bytes()
    if len(data) < 4 + 4 + 8 + 64:
        print("Erreur : fichier trop petit pour contenir un container Verbum.", file=sys.stderr)
        return 1

    magic = data[:4]
    if magic != MAGIC:
        print(f"Erreur : magic incorrect (attendu VRBM, trouvé {magic!r}).", file=sys.stderr)
        return 1

    version = struct.unpack("<I", data[4:8])[0]
    if version != VERSION:
        print(f"Erreur : version non supportée ({version}, attendu {VERSION}).", file=sys.stderr)
        return 1

    body_len = struct.unpack("<Q", data[8:16])[0]
    expected_total = 4 + 4 + 8 + body_len + 64
    if len(data) != expected_total:
        print(
            f"Erreur : taille de fichier ({len(data)}) ne correspond pas à body_len ({body_len}). "
            f"Attendu = {expected_total}.",
            file=sys.stderr,
        )
        return 1

    signed_payload = data[: 16 + body_len]  # magic + version + body_len + body
    signature = data[16 + body_len :]

    try:
        pub_key.verify(signature, signed_payload)
    except InvalidSignature:
        print("✗ Signature INVALIDE — le corpus a été modifié ou la clé ne correspond pas.")
        return 1

    sha256 = hashlib.sha256(data).hexdigest()

    print("✓ Signature Ed25519 VALIDE.")
    print(f"  Fichier        : {corpus_path}")
    print(f"  Taille         : {len(data):,} octets")
    print(f"  Body length    : {body_len:,} octets")
    print(f"  SHA-256        : {sha256}")
    print(f"  Clé publique   : {pub_bytes.hex()}")
    return 0


def main() -> int:
    if len(sys.argv) != 3:
        print(__doc__, file=sys.stderr)
        return 2
    return verify(Path(sys.argv[1]), Path(sys.argv[2]))


if __name__ == "__main__":
    sys.exit(main())
