from __future__ import annotations

import io
from typing import TYPE_CHECKING
from unittest.mock import patch

import pytest

from dissect.target.helpers import keychain
from dissect.target.plugins.os.windows.dpapi.crypto import (
    _AES256,
    _DES3,
    _SHA1,
    _SHA512,
)
from dissect.target.plugins.os.windows.dpapi.dpapi import DPAPIBlob, DPAPIPlugin, MasterKeyFile
from tests._utils import absolute_path
from tests.conftest import add_win_user
from tests.plugins.os.windows.test__os import map_version_value
from tests.plugins.os.windows.test_lsa import (
    POLICY_KEY_PATH_NT5,
    POLICY_KEY_PATH_NT6,
    map_lsa_polkey,
    map_lsa_secrets,
    map_lsa_system_keys,
)

if TYPE_CHECKING:
    from dissect.target.filesystem import VirtualFilesystem
    from dissect.target.helpers.regutil import VirtualHive
    from dissect.target.target import Target

"""
You can use the following PowerShell code to generate user and SYSTEM DPAPI encrypted blobs.
This has been tested on every major Windows release since XP and works on PowerShell 1.0 and up.

.. code-block:: text

    "example text" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Set-Content -Path out.txt

To create mock Windows targets you'll need the following additional data:
    - add_win_user: SID of the user the secret was generated by / for
    - map_lsa_system_keys: LSA system keys
    - map_lsa_polkey: LSA policy key
    - map_lsa_secrets: LSA DPAPI_SYSTEM encrypted secret
    - DPAPI master key that was used for encrypting the blob
    - The user's password or secret used to encrypt the DPAPI master key
"""


@pytest.mark.parametrize(
    ("user_type", "master_key_guid", "enc_data", "plaintext"),
    [
        pytest.param(
            "user",
            "f5638f3b-9632-480b-ba9c-c73bdabe9f86",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb010000003b8f63f532960b48"
                "ba9cc73bdabe9f8600000000020000000000106600000001000020000000d077"
                "d64b2d9bba2d3bc621a9a58d59de28aac032417f13ed1c8036503e4a3bce0000"
                "00000e8000000002000020000000043484aef65bad8467509d09906fe98601b4"
                "1cc04a7d417e83b0b00c02a81421500000004650eecc2f3bf3f6b95dafa29f1c"
                "2595f35a28b2f4a76c0a984e0aa65db83705517e5f98ce0de9f3a1abffe711f1"
                "b8129deab037dd790125542ba520f8f6ba0d863f36aea01b5e9222e98de1aeee"
                "9dbd400000005b938c2d3433effaaa23f04e5c272e8bd0877073a83cf43aed75"
                "82d22c59522856e2dfb6585bc82c4c66081d01f0b8fbfdc03641db3e6e195c47"
                "3940324b68b9"
            ),
            "this is a windows 10 user secret!",
            id="user",
        ),
        pytest.param(
            "system",
            "59873e79-ca24-459a-8b17-a5dcd82c6ea1",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000793e875924ca9a45"
                "8b17a5dcd82c6ea100000000020000000000106600000001000020000000bf65"
                "33a2eaa9c0393173bc5b8b53c33bb6273b8b86f6586a64a3cb5a2dbab7190000"
                "00000e80000000020000200000006c29f425bf48b2bf66d9fe2c6c406a48c41a"
                "77c90e911fe6576bc9811553ef1d50000000e5bf02ccca4cc273aa5dfacfa64b"
                "c3ab746f59de5699943edbe0fdb6b389c73e0541589d4ac9f37cf4488c44bf0f"
                "7fc656dc2bcad919c112a8cda9b54ede0d31ea260067f5540c0eecb3322682e2"
                "0b6b400000009a07d60ab8ef3632b8f54009ece9599beba174626c8fe0dd8642"
                "bd207dde9a6c34f1f611eccb6606d1e1ea10a406930b4e11087031dbe083a66e"
                "e5db2b95ff71"
            ),
            "this is a windows 10 system secret!",
            id="system",
        ),
    ],
)
def test_dpapi_decrypt_blob_win_10(
    target_win: Target,
    fs_win: VirtualFilesystem,
    hive_hklm: VirtualHive,
    hive_hku: VirtualHive,
    user_type: str,
    master_key_guid: str,
    enc_data: str,
    plaintext: str,
) -> None:
    """Test decrypting a windows 10 dpapi system and user blob."""

    add_win_user(
        hive_hklm,
        hive_hku,
        target_win,
        sid="S-1-5-21-1555088973-3915578919-3195617063-1003",
        home="c:\\users\\user",
    )

    map_lsa_system_keys(hive_hklm, {"Data": "3b03ce4f", "GBG": "7bf5e093", "JD": "51fe8c74", "Skew1": "3e14f655"})
    map_lsa_polkey(
        hive_hklm,
        POLICY_KEY_PATH_NT6,
        bytes.fromhex(
            "00000001ecffe17b2a997440aa939adbff26f1fc030000000000000061f61040"
            "72b7b9117289a891bc1edb9fa83fafae5d8648b8674825dc9bdcf1bf5ffbe591"
            "27fd3a1baca6c5447530574c27a41ab721b13e54a9af857e682b8458859c0fab"
            "55461a8f1b2b8c12d8f186a80991012e2d3ede5b1167554c726e404ed0eb96f6"
            "488b90ece5b0b3ef9bf244f35dd65a30b2b450c4e227838810b631564b89300e"
            "d4355040f23890f8b6b8eab8"
        ),
    )
    map_lsa_secrets(
        hive_hklm,
        {
            "DPAPI_SYSTEM": bytes.fromhex(
                "00000001001f9b85984f68a8ed3e9d44dbd5b79c0300000000000000835b4ff6"
                "6e74154ffab75afd31a2860616c87411bc97d368a068fca62d1564ae1396f88e"
                "06de87b5e5632c668538ee36c75f67cee98d49ebc1e88fa7e9be16144af31e8e"
                "5fd78329279e50d792e8a6b35a59cb55016748ecd8e12f148b1d32b3"
            )
        },
    )

    # user master key
    fs_win.map_file(
        "Users/user/AppData/Roaming/Microsoft/Protect/S-1-5-21-1555088973-3915578919-3195617063-1003/f5638f3b-9632-480b-ba9c-c73bdabe9f86",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/f5638f3b-9632-480b-ba9c-c73bdabe9f86"),
    )

    # system master key
    fs_win.map_file(
        "Windows/System32/Microsoft/Protect/S-1-5-18/User/59873e79-ca24-459a-8b17-a5dcd82c6ea1",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/59873e79-ca24-459a-8b17-a5dcd82c6ea1"),
    )

    map_version_value(target_win, "CurrentVersion", 10.0)
    target_win.add_plugin(DPAPIPlugin)

    # test parsing dpapi system blob
    enc_data = bytes.fromhex(enc_data)
    blob = DPAPIBlob(enc_data)
    assert blob.version == 1
    assert blob.provider == "df9d8cd0-1501-11d1-8c7a-00c04fc297eb"
    assert blob.guid == master_key_guid
    assert isinstance(blob.cipher_algorithm, _AES256)
    assert isinstance(blob.hash_algorithm, _SHA512)

    if user_type == "user":
        # test decrypting dpapi user blob
        keychain.register_key(
            key_type=keychain.KeyType.PASSPHRASE,
            value="password",
            identifier=None,
            provider="user",
        )
        decrypted = target_win.dpapi.decrypt_user_blob(enc_data, sid="S-1-5-21-1555088973-3915578919-3195617063-1003")
    elif user_type == "system":
        # test decrypting dpapi system blob
        decrypted = target_win.dpapi.decrypt_system_blob(enc_data)

    assert decrypted.decode("utf-16-le") == plaintext


@pytest.mark.parametrize(
    ("user_type", "master_key_guid", "enc_data", "plaintext"),
    [
        pytest.param(
            "user",
            "835419eb-ce4a-4ae3-a72a-35c1383a6519",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000eb1954834acee34a"
                "a72a35c1383a65190000000002000000000010660000000100002000000093f6"
                "c9a6ac508949a451976699e226145277e67f88fd1bea46576df1f8386fc10000"
                "00000e80000000020000200000003ff5eae86a8f83bb0517a0706535b1107d9f"
                "e72682ca4f68b7dfbc56939f0ef530000000ab26fca786c57987910bd313da0c"
                "0426b42d139d4b9904198f45ca51996eb6c888b9ba50d5787d18fb56826db726"
                "e8fd400000009ee887a2f48d5812921dc870691b49b94d368a03c4f55cdceb00"
                "fa5c427b7c4f151d5a6de35b8902d18698905f0abd89e03bbc948906977ff5c4"
                "e40d07649869"
            ),
            "this is a user secret!",
            id="user",
        ),
        pytest.param(
            "system",
            "ab15130c-61cd-4065-be48-faba655f80d1",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb010000000c1315abcd616540"
                "be48faba655f80d100000000020000000000106600000001000020000000511a"
                "83f59218606393ea986893f8d39156eed29ad43c110f171ad5e1aa63a8a40000"
                "00000e800000000200002000000029cb546db6b46d4407ead0366bd7a5c52e97"
                "eebc1291128d7d19bfdf9736449a400000005ec4e78e95d88d4ca584a23e506e"
                "e0677aec069c38631dd9c7d49559cfeb60ed4962fab7bfd686aa06718c9be667"
                "d1126169baa4c5d985dcb029c2d3febd6e3f400000003829a0d94657938a8f29"
                "a399931f9bddc2208f8dc9e7940b494147502793dcb38c4265943d77d8aac204"
                "a80b813315de746d3334e7abb3bddae312a87b132384"
            ),
            "this is a system secret!",
            id="system",
        ),
    ],
)
def test_dpapi_decrypt_blob_win_7(
    target_win: Target,
    fs_win: VirtualFilesystem,
    hive_hklm: VirtualHive,
    hive_hku: VirtualHive,
    user_type: str,
    master_key_guid: str,
    enc_data: str,
    plaintext: str,
) -> None:
    """Test decrypting a windows 7 user and system blob."""

    add_win_user(hive_hklm, hive_hku, target_win, sid="S-1-5-18", home="%systemroot%\\system32\\config\\systemprofile")
    add_win_user(
        hive_hklm,
        hive_hku,
        target_win,
        sid="S-1-5-21-2423314426-1454842194-222297899-1000",
        home="C:\\Users\\user",
    )

    map_lsa_system_keys(hive_hklm, {"Data": "7ea9249d", "GBG": "32007b89", "JD": "1f884e65", "Skew1": "f5b653a9"})
    map_lsa_polkey(
        hive_hklm,
        POLICY_KEY_PATH_NT6,
        bytes.fromhex(
            "00000001ecffe17b2a997440aa939adbff26f1fc0300000000000000866bf805"
            "f051a5a0a4aa4fc6e8d460b9c2106b47adf9096f610d84da45a9c092cba0787d"
            "1bc4d161ebcec3f16ac86ba57de34aec8f5c1fc950998d3ad05160b2aa9df553"
            "5ec99c36cd00d6cbef7becc50d4e320a6722fbb6f4fd26cdaf9a78205a7b8481"
            "c6e51f6431081d602fc1e77d24f38d0c0e3eb3ceca952efbe2635960e9ac1995"
            "e1a0380659bf1a8c5226b7d1"
        ),
    )

    map_lsa_secrets(
        hive_hklm,
        {
            "DPAPI_SYSTEM": bytes.fromhex(
                "00000001f9da1883a8c8a2e5f8c9e783b65ef8e90300000000000000f293e237"
                "48961ef80e666f05317889b6d86143400b3282cc087dcc8a6a3f6213d6e945c8"
                "e323903818451e15d09201a50ed43c629acdb20d23424bc2625acfdf9b94ed9f"
                "3868463f6c7adc8736a8cc4e70e115c621c2d482f07d501d21af32b9"
            ),
            "DefaultPassword": bytes.fromhex(
                "00000001f9da1883a8c8a2e5f8c9e783b65ef8e903000000000000003f0e05667"
                "69d976dcabfc1c10571bc0c6a6ca5032a258c28c74fc7646680bceafcb70fd7de"
                "e974854835260283fcb0dc3af665763b7757049c01730212a5cace00000000000"
                "000000000000000000000"
            ),
        },
    )

    # user master key
    fs_win.map_file(
        "Users/user/AppData/Roaming/Microsoft/Protect/S-1-5-21-2423314426-1454842194-222297899-1000/835419eb-ce4a-4ae3-a72a-35c1383a6519",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/835419eb-ce4a-4ae3-a72a-35c1383a6519"),
    )

    # system master key
    fs_win.map_file(
        "Windows/System32/Microsoft/Protect/S-1-5-18/User/ab15130c-61cd-4065-be48-faba655f80d1",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/ab15130c-61cd-4065-be48-faba655f80d1"),
    )

    map_version_value(target_win, "CurrentVersion", 6.1)
    target_win.add_plugin(DPAPIPlugin)

    # test dpapi blob parsing
    enc_data = bytes.fromhex(enc_data)
    blob = DPAPIBlob(enc_data)
    assert blob.version == 1
    assert blob.guid == master_key_guid
    assert blob.provider == "df9d8cd0-1501-11d1-8c7a-00c04fc297eb"
    assert isinstance(blob.cipher_algorithm, _AES256)
    assert isinstance(blob.hash_algorithm, _SHA512)

    if user_type == "user":
        # test decrypting dpapi blob using user DefaultPassword lsa secret
        decrypted = target_win.dpapi.decrypt_user_blob(enc_data, sid="S-1-5-21-2423314426-1454842194-222297899-1000")

    elif user_type == "system":
        # test decrypting dpapi blob using DPAPI_SYSTEM lsa secret
        decrypted = target_win.dpapi.decrypt_system_blob(enc_data)

    assert decrypted.decode("utf-16-le") == plaintext


@pytest.mark.parametrize(
    ("user_type", "master_key_guid", "enc_data", "plaintext"),
    [
        pytest.param(
            "user",
            "8bd87dd9-10fa-40b5-8614-5d0a7e8911c5",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000d97dd88bfa10b540"
                "86145d0a7e8911c50000000002000000000003660000a800000010000000c58b"
                "6464cb47b91908f333a4947bc9770000000004800000a000000010000000fa89"
                "775be164fdbe5de900710788dd075000000020a462e392783abaadaec0b42b74"
                "dd729f19e7bb4cb6bed585e4f11d332d52a5e35d998d63b350172eae76a595e8"
                "cc084dff8389900fd0a560d192d2e982b83382bc39bc37527c7eee989624fef5"
                "91941400000070e42b6873f1372abd5dbaced1e97dd0fbeee5c0"
            ),
            "this is a windows vista user secret!",
            id="user",
        ),
        pytest.param(
            "system",
            "9ed75ec3-d074-4a7a-a428-1b70188cce4c",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000c35ed79e74d07a4a"
                "a4281b70188cce4c0000000002000000000003660000a80000001000000003e3"
                "f985868f3a9e6e1628672bc702610000000004800000a000000010000000ffa0"
                "2bf52d9cbfd640921b60560e586c50000000b0705dd3c0975c5a21de7d77785b"
                "756b0a6ee22fe7ee080730fa213630186ccc96c98d6cfd4903cd0ba0008016f9"
                "99cc6d3ca8375e42561b1289e3db3351784dd9b268163998ba61b190abf50bf1"
                "bea5140000003ec445826ec3c1dce592a6b15d467290ca1f4068"
            ),
            "this is a windows vista system secret!",
            id="system",
        ),
    ],
)
def test_dpapi_decrypt_blob_win_vista(
    target_win: Target,
    fs_win: VirtualFilesystem,
    hive_hklm: VirtualHive,
    hive_hku: VirtualHive,
    user_type: str,
    master_key_guid: str,
    enc_data: str,
    plaintext: str,
) -> None:
    """Test decrypting a windows vista user and system blob."""
    add_win_user(hive_hklm, hive_hku, target_win, sid="S-1-5-18", home="%systemroot%\\system32\\config\\systemprofile")
    add_win_user(
        hive_hklm, hive_hku, target_win, sid="S-1-5-21-3694181269-2347385980-100747364-1000", home="C:\\Users\\user"
    )

    map_lsa_system_keys(
        hive_hklm,
        {
            "Data": "c948a244",
            "GBG": "f72a0d84",
            "JD": "4974e288",
            "Skew1": "485dc08b",
        },
    )
    map_lsa_polkey(
        hive_hklm,
        POLICY_KEY_PATH_NT6,
        bytes.fromhex(
            "00000001ecffe17b2a997440aa939adbff26f1fc03000000000000007cf4b483"
            "1c3fb8be797fc9f30e4226bf3526520458eacb05b3dd0db072b07d25683ee35e"
            "69a7461ebd65593ffd3dc0e5be31b31deec2573dc6367258783e0394d1b55a2b"
            "e6d7931881eeb391da41a062e17419a35e1b0000b2717a362cbc81e89b54f85c"
            "801a1a46a492491032e7055aa46730d3d78530834547b1c6ca42da34d5dacac9"
            "b9ce6013c3486f925401a97b"
        ),
    )
    map_lsa_secrets(
        hive_hklm,
        {
            "DPAPI_SYSTEM": bytes.fromhex(
                "00000001340de2496d244a12a38743bdb773cfd203000000000000006d926cee"
                "7ae8a462255cfbd47b7eae097dd9104d61591cca7e7973eecbb5bbc468f375856"
                "a63f6d5caa1f8f796220d12117cf455f85219a9f8ab0c04fd553529e257baa17b"
                "b3e81c5cc62af835e7332565b38b1a97f590df056044ad38c2e88f"
            ),
            "DefaultPassword": bytes.fromhex(
                "00000001340de2496d244a12a38743bdb773cfd2030000000000000094eb76f9"
                "2b49a37b98cd19f98ab5372eab0e18dbdc6e36729f31961a77197c69c4115cd3"
                "7dd0cf2e7a002453c9280bb700811db5fc09517bc7eb387f8aebee6a00000000"
                "000000000000000000000000"
            ),
        },
    )

    # user master key
    fs_win.map_file(
        "Users/user/AppData/Roaming/Microsoft/Protect/S-1-5-21-3694181269-2347385980-100747364-1000/8bd87dd9-10fa-40b5-8614-5d0a7e8911c5",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/8bd87dd9-10fa-40b5-8614-5d0a7e8911c5"),
    )

    # system master key
    fs_win.map_file(
        "Windows/System32/Microsoft/Protect/S-1-5-18/User/9ed75ec3-d074-4a7a-a428-1b70188cce4c",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/9ed75ec3-d074-4a7a-a428-1b70188cce4c"),
    )

    map_version_value(target_win, "CurrentVersion", 6.0)
    target_win.add_plugin(DPAPIPlugin)

    enc_data = bytes.fromhex(enc_data)

    blob = DPAPIBlob(enc_data)
    assert blob.version == 1
    assert blob.provider == "df9d8cd0-1501-11d1-8c7a-00c04fc297eb"
    assert blob.guid == master_key_guid
    assert blob.description == "\x00"
    assert isinstance(blob.cipher_algorithm, _DES3)
    assert isinstance(blob.hash_algorithm, _SHA1)

    if user_type == "user":
        # test decrypting dpapi blob using user DefaultPassword lsa secret
        decrypted = target_win.dpapi.decrypt_user_blob(enc_data, username="user")
    elif user_type == "system":
        # test decrypting dpapi blob using DPAPI_SYSTEM lsa secret
        decrypted = target_win.dpapi.decrypt_system_blob(enc_data)

    assert decrypted.decode("utf-16-le") == plaintext


@pytest.mark.parametrize(
    ("user_type", "master_key_guid", "enc_data", "plaintext"),
    [
        pytest.param(
            "user",
            "e1245b49-43d9-465f-b161-eab88c27620d",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000495b24e1d9435f46"
                "b161eab88c27620d0000000002000000000003660000a800000010000000631f"
                "9403ae62478940353bc54274d0bd0000000004800000a00000001000000081aa"
                "f6b9a2f370073f8f869269607dd348000000312f2f35f6751e099685f93e43b5"
                "9f77e7bd0202e0efb2ed1412a5f9605a7b1229e379fcb6b6de141eee7e7eaa14"
                "ff14899edb6e805903ddd81c2b1b65cfa8246ce3da28dd3a1c4214000000d9d1"
                "f626a57c4b1c4dd15267560b945c4c7e9124"
            ),
            "this is a windows xp user secret!",
            id="user",
        ),
        pytest.param(
            "system",
            "2ee95631-ed37-43f9-8a08-378924a60de7",
            (
                "01000000d08c9ddf0115d1118c7a00c04fc297eb010000003156e92e37edf943"
                "8a08378924a60de70000000002000000000003660000a8000000100000000e23"
                "be00ab6ef398df788e3fbcafbdcc0000000004800000a0000000100000005af1"
                "9111a4f165ffebb7bbf4133f634b4800000090605c23c6a9225592f6e6e75aa2"
                "32566a2d36037ebdacef182aaa677c0b64e541ca1c59732125b0149f0031c48f"
                "3d69c2fde832fa304d0b7af22b6fda2a6228507988d7cfe49dbf140000003c75"
                "50be593b511450387842969ee1217fc07870"
            ),
            "this is a windows xp system secret!",
            id="system",
        ),
    ],
)
def test_dpapi_decrypt_blob_win_xp(
    target_win: Target,
    fs_win: VirtualFilesystem,
    hive_hklm: VirtualHive,
    hive_hku: VirtualHive,
    user_type: str,
    master_key_guid: str,
    enc_data: str,
    plaintext: str,
) -> None:
    """Test decrypting a windows xp user and system blob."""

    add_win_user(hive_hklm, hive_hku, target_win, sid="S-1-5-18", home="%systemroot%\\system32\\config\\systemprofile")
    add_win_user(
        hive_hklm,
        hive_hku,
        target_win,
        sid="S-1-5-21-1960408961-57989841-725345543-1003",
        home="C:\\Documents and Settings\\user",
    )

    map_lsa_system_keys(hive_hklm, {"Data": "0e1eaddb", "GBG": "36ec555b", "JD": "8397b619", "Skew1": "9dfad6c8"})
    map_lsa_polkey(
        hive_hklm,
        POLICY_KEY_PATH_NT5,
        bytes.fromhex(
            "0100000001000000000000003e4253c6ab72f73306ffe45a33a4ec5d47d21483"
            "85be6036b4c5bab2666d7f5793f23fed8513577b79abddb40eaa92c7a19b34ab"
            "b9c134e88bf5dd2b2fbf6356"
        ),
    )

    map_lsa_secrets(
        hive_hklm,
        {
            "DPAPI_SYSTEM": bytes.fromhex(
                "3800000038000020acb90c00a26440bdf0382bdbcb5d51a743cb4cca01cfecb3"
                "202848e00dfae25f8f0f3f4710ff37828532b5f600e97f6a00528fd8e0137f49"
                "6fd2d905"
            ),
        },
    )

    # user master key
    fs_win.map_file(
        "Documents and Settings/user/Application Data/Microsoft/Protect/S-1-5-21-1960408961-57989841-725345543-1003/e1245b49-43d9-465f-b161-eab88c27620d",  # noqa: E501
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/e1245b49-43d9-465f-b161-eab88c27620d"),
    )

    # system master key
    fs_win.map_file(
        "Windows/System32/Microsoft/Protect/S-1-5-18/User/2ee95631-ed37-43f9-8a08-378924a60de7",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/2ee95631-ed37-43f9-8a08-378924a60de7"),
    )

    map_version_value(target_win, "CurrentVersion", 5.1)
    target_win.add_plugin(DPAPIPlugin)

    # make sure the user master key is decrypted without providing a password (empty keyprovider)
    user_mk = target_win.dpapi.master_keys.get("S-1-5-21-1960408961-57989841-725345543-1003", {}).get(
        "e1245b49-43d9-465f-b161-eab88c27620d"
    )
    assert isinstance(user_mk, MasterKeyFile)
    assert user_mk.decrypted

    enc_data = bytes.fromhex(enc_data)

    blob = DPAPIBlob(enc_data)
    assert blob.version == 1
    assert blob.provider == "df9d8cd0-1501-11d1-8c7a-00c04fc297eb"
    assert blob.guid == master_key_guid
    assert blob.description == "\x00"
    assert isinstance(blob.cipher_algorithm, _DES3)
    assert isinstance(blob.hash_algorithm, _SHA1)

    if user_type == "user":
        # test decrypting dpapi blob using user master key
        decrypted = target_win.dpapi.decrypt_user_blob(enc_data, username="user")

    elif user_type == "system":
        # test decrypting dpapi blob using DPAPI_SYSTEM lsa secret
        decrypted = target_win.dpapi.decrypt_system_blob(enc_data)

    assert decrypted.decode("utf-16-le") == plaintext


def test_dpapi_master_keys_deduplicate(
    target_win: Target, fs_win: VirtualFilesystem, hive_hklm: VirtualHive, hive_hku: VirtualHive
) -> None:
    """Test if we correctly deduplicate master keys."""

    add_win_user(
        hive_hklm,
        hive_hku,
        target_win,
        sid="S-1-5-21-1555088973-3915578919-3195617063-1003",
        home="c:\\users\\user",
    )

    # Mock "Application Data" -> "AppData/Roaming" symlink
    fs_win.symlink("/sysvol/Users/user/AppData/Roaming", "/Users/user/Application Data")

    fs_win.map_file(
        "Users/user/AppData/Roaming/Microsoft/Protect/S-1-5-21-1555088973-3915578919-3195617063-1003/f5638f3b-9632-480b-ba9c-c73bdabe9f86",
        absolute_path("_data/plugins/os/windows/dpapi/master_keys/f5638f3b-9632-480b-ba9c-c73bdabe9f86"),
    )

    plugin = target_win.add_plugin(DPAPIPlugin, check_compatible=False)

    with patch("dissect.target.plugins.os.windows.dpapi.dpapi.MasterKeyFile") as MasterKeyFileMock:
        dict(plugin.master_keys)
        MasterKeyFileMock.assert_called_once()


def test_dpapi_master_key_decrypt_with_hash(
    target_win: Target, fs_win: VirtualFilesystem, hive_hklm: VirtualHive, hive_hku: VirtualHive
) -> None:
    """Test that 'master_keys' are decrypted first with a password and then with a hash."""
    # Setup: create a fake master key file and hash
    sid = "S-1-5-21-1111111111-2222222222-3333333333-1001"
    mk_guid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
    mk_hash = "0123456789abcdef0123456789abcdef"  # 16 bytes hex string

    add_win_user(hive_hklm, hive_hku, target_win, sid=sid, home="C:\\Users\\user")
    map_lsa_system_keys(
        hive_hklm,
        {"Data": "deadbeef", "GBG": "cafebabe", "JD": "feedface", "Skew1": "badc0de1"},
    )

    # Register the hash in the keychain
    class MyStr(str):
        __slots__ = ()

    keychain.register_key(
        key_type=keychain.KeyType.PASSPHRASE,
        value=MyStr(mk_hash),
        identifier=None,
        provider="user",
    )

    # Map a dummy master key file using an in-memory BytesIO
    fs_win.map_file_fh(f"Users/user/AppData/Roaming/Microsoft/Protect/{sid}/{mk_guid}", io.BytesIO(b"\x00" * 128))

    map_version_value(target_win, "CurrentVersion", 10.0)
    target_win.add_plugin(DPAPIPlugin)

    # Patch MasterKeyFile to track decrypted state in a closure
    decrypted_state = False

    def fake_decrypt_with_password(self: MasterKeyFile, *args, **kwargs) -> bool:
        nonlocal decrypted_state
        decrypted_state = False
        return False

    def fake_decrypt_with_hash(self: MasterKeyFile, user_sid: str, password_hash: bytes) -> bool:
        nonlocal decrypted_state
        decrypted_state = len(password_hash) > 0
        return decrypted_state

    def decrypted_getter(self: MasterKeyFile) -> bool:
        return decrypted_state

    with (
        patch.object(MasterKeyFile, "decrypt_with_password", new=fake_decrypt_with_password),
        patch.object(MasterKeyFile, "decrypt_with_hash", new=fake_decrypt_with_hash),
        patch.object(MasterKeyFile, "decrypted", new=property(decrypted_getter)),
    ):
        # 'master_keys' is the subject under test: accessing it should trigger the PyPy workaround
        keys = target_win.dpapi.master_keys
        assert sid in keys
        assert mk_guid in keys[sid]
        assert decrypted_state
