#!/usr/bin/env python3
"""
poc_recover_plaintext.py : demontre la recuperation, SANS AUCUNE CLE, des
deux tiers du contenu d'un fichier chiffre par Prince-Ransomware (voir
writeup.md section 5 pour l'analyse qui etablit ce motif par decompilation
+ desassemblage ABI, sur l'echantillon reel).

Principe : le contenu chiffre suit un motif fixe, un octet transforme
suivi de deux octets copies tels quels (en clair), repete sur toute la
longueur. Ce script :
  1. localise le debut du contenu (apres les deux marqueurs "||" qui
     separent les enveloppes ECIES cle/nonce du contenu, cf format
     documente dans runbook.md) ;
  2. extrait directement, sans dechiffrement, tous les octets a une
     position non multiple de 3 (relative au debut du contenu) ;
  3. rapporte le taux de recuperation et ecrit une reconstruction
     partielle (octets non recuperes remplaces par 0x00) pour inspection.

Limite assumee (voir writeup.md section 7) : la recherche du marqueur
"||" par simple correspondance de sous-chaine peut theoriquement
correspondre par coincidence a l'interieur d'une enveloppe ECIES
(donnees a haute entropie) ; sur un corpus reel, determiner une fois pour
toutes la taille exacte des enveloppes ECIES (fixe pour un schema et une
taille de plaintext donnes) serait plus robuste qu'une recherche de
delimiteur. Suffisant pour cette demonstration.

Usage : poc_recover_plaintext.py FICHIER_CHIFFRE FICHIER_SORTIE_PARTIEL
"""
import sys


def main():
    if len(sys.argv) != 3:
        sys.exit(__doc__)
    src, dst = sys.argv[1], sys.argv[2]

    with open(src, "rb") as f:
        data = f.read()

    parts = data.split(b"||", 2)
    if len(parts) != 3:
        sys.exit(f"format inattendu : {len(parts) - 1} marqueur(s) '||' trouve(s), 2 attendus")

    _key_envelope, _nonce_envelope, content = parts
    print(f"enveloppe cle ECIES : {len(_key_envelope)} octets (ignoree, pas necessaire)")
    print(f"enveloppe nonce ECIES : {len(_nonce_envelope)} octets (ignoree, pas necessaire)")
    print(f"contenu : {len(content)} octets")

    recovered = bytearray(len(content))
    n_recovered = 0
    for i in range(len(content)):
        if i % 3 != 0:
            recovered[i] = content[i]
            n_recovered += 1
        else:
            recovered[i] = 0x00  # position reellement chiffree, inconnue sans la cle privee

    pct = 100.0 * n_recovered / len(content) if content else 0.0
    print(f"\n{n_recovered}/{len(content)} octets recuperes sans aucune cle ({pct:.1f}%)")

    with open(dst, "wb") as f:
        f.write(recovered)
    print(f"reconstruction partielle ecrite dans {dst} (octets non recuperes = 0x00)")


if __name__ == "__main__":
    main()
