#!/usr/bin/env python3
"""Weryfikacja pieczeci CYRBER. Bez zaleznosci, bez dostepu do naszego API.

Skrypt odtwarza drzewo Merkle z opublikowanych lisci i porownuje korzen
z tym, ktory podaje pieczec. Jesli zgodne, plik lisci nie zostal ruszony
po zapieczetowaniu. Kazda zmiana jednego liscia zmienia korzen.

    python3 weryfikuj.py pieczec.json

Zrodlo algorytmu: modules/merkle.py w kodzie CYRBER. Para wezlow jest
sortowana przed zlaczeniem, a przy nieparzystej liczbie wezlow ostatni
duplikuje sam siebie.

Uwaga: ten skrypt dowodzi integralnosci listy findingow, czyli tego, ze
nikt jej po pieczeci nie podmienil. Nie orzeka, ze kazdy finding jest
trafny. Trafnosci pilnuja inne mechanizmy, opisane na cyrber.com/poligon.
"""

import hashlib
import json
import sys


def sha256_hex(text):
    return hashlib.sha256(text.encode("utf-8")).hexdigest()


def hash_pair(a, b):
    return sha256_hex(min(a, b) + max(a, b))


def merkle_root(leaf_hashes):
    if not leaf_hashes:
        return sha256_hex("empty")
    level = list(leaf_hashes)
    while len(level) > 1:
        if len(level) % 2 == 1:
            level.append(level[-1])
        level = [hash_pair(level[i], level[i + 1]) for i in range(0, len(level), 2)]
    return level[0]


def main(path):
    with open(path, encoding="utf-8") as fh:
        pkg = json.load(fh)

    seal = pkg["pieczec"]
    leaves = sorted(pkg["leaves"], key=lambda leaf: leaf["index"])

    if len(leaves) != seal["leaves_count"]:
        print("NIEZGODNOSC: pieczec deklaruje %d lisci, plik ma %d"
              % (seal["leaves_count"], len(leaves)))
        return 1

    computed = merkle_root([leaf["hash"] for leaf in leaves])
    expected = seal["root_hash"]

    print("cel            : %s" % pkg["przebieg"]["target"])
    print("zapieczetowano : %s" % seal["sealed_at"])
    print("liscie         : %d" % len(leaves))
    print("korzen z pliku : %s" % expected)
    print("korzen liczony : %s" % computed)
    print()

    if computed == expected:
        print("VERIFIED")
        return 0
    print("FAILED")
    return 1


if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("uzycie: python3 weryfikuj.py pieczec.json")
    sys.exit(main(sys.argv[1]))
