#!/usr/bin/env python3
"""Create a Chrome profile with one saved password for the victim origin.

The victim origin (https://login.example.com:8443/) is a *different* origin
from the attacker page (https://attacker.example.com:8443/). They share the
same eTLD+1 (example.com) so the password store returns it as a PSL match.

We let Chrome create the empty profile (so the Login Data schema is correct
for this build), then INSERT directly into the SQLite DB. password_value is
encrypted with the Linux --password-store=basic v10 key
(AES-128-CBC, key=PBKDF2("peanuts","saltysalt",1), IV=16 spaces).
"""
import os
import shutil
import sqlite3
import subprocess
import sys
import time

CHROME = os.environ.get('CHROME', '/chromium/chromium/src/out/asan/chrome')
PROFILE = os.environ.get('PROFILE', '/tmp/chrome-poc-profile')

VICTIM_ORIGIN = 'https://login.example.com:8443/'
VICTIM_USER = 'victim_user'
VICTIM_PASS = 'VictimSecret123!'

# PBKDF2-HMAC-SHA1("peanuts", "saltysalt", 1 iter, 16 bytes)
V10_KEY_HEX = 'fd621fe5a2b402539dfa147ca9272778'
V10_IV_HEX = '20' * 16  # 16 ASCII spaces


def encrypt_v10(plaintext: str) -> bytes:
    ct = subprocess.check_output(
        ['openssl', 'enc', '-aes-128-cbc', '-K', V10_KEY_HEX,
         '-iv', V10_IV_HEX],
        input=plaintext.encode('utf-8'))
    return b'v10' + ct


def main():
    if os.path.isdir(PROFILE):
        shutil.rmtree(PROFILE)
    print(f'[setup] launching chrome once to create profile at {PROFILE}')
    p = subprocess.Popen(
        [CHROME, '--no-sandbox', '--no-first-run',
         '--no-default-browser-check', '--password-store=basic',
         '--user-data-dir=' + PROFILE, 'about:blank'],
        stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    login_db = os.path.join(PROFILE, 'Default', 'Login Data')
    for _ in range(60):
        if os.path.exists(login_db) and os.path.getsize(login_db) > 0:
            break
        time.sleep(0.5)
    time.sleep(2)
    p.terminate()
    try:
        p.wait(10)
    except subprocess.TimeoutExpired:
        p.kill()
    time.sleep(1)

    if not os.path.exists(login_db):
        print('[setup] ERROR: Login Data not created', file=sys.stderr)
        sys.exit(1)

    # Remove journals so SQLite doesn't roll back our insert.
    for j in ('Login Data-journal', 'Login Data-wal', 'Login Data-shm'):
        jp = os.path.join(PROFILE, 'Default', j)
        if os.path.exists(jp):
            os.remove(jp)

    pw_blob = encrypt_v10(VICTIM_PASS)
    print(f'[setup] inserting credential for {VICTIM_ORIGIN} '
          f'(user={VICTIM_USER}, pw_blob={len(pw_blob)} bytes)')

    conn = sqlite3.connect(login_db)
    cur = conn.cursor()
    now_us = int(time.time() * 1_000_000)  # microseconds
    cur.execute(
        '''INSERT INTO logins (
            origin_url, action_url, username_element, username_value,
            password_element, password_value, submit_element, signon_realm,
            date_created, blacklisted_by_user, scheme, password_type,
            times_used, form_data, display_name, icon_url, federation_url,
            skip_zero_click, generation_upload_status, possible_username_pairs,
            date_last_used, moving_blocked_for, date_password_modified,
            sender_email, sender_name, date_received,
            sharing_notification_displayed, keychain_identifier,
            sender_profile_image_url, date_last_filled, actor_login_approved
        ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
        (
            VICTIM_ORIGIN,             # origin_url
            VICTIM_ORIGIN,             # action_url
            '',                        # username_element
            VICTIM_USER,               # username_value
            '',                        # password_element
            pw_blob,                   # password_value (encrypted)
            '',                        # submit_element
            VICTIM_ORIGIN,             # signon_realm
            now_us,                    # date_created
            0,                         # blacklisted_by_user
            0,                         # scheme = kHtml
            0,                         # password_type
            1,                         # times_used
            b'',                       # form_data (empty -> skipped)
            '',                        # display_name
            '',                        # icon_url
            '',                        # federation_url
            1,                         # skip_zero_click (irrelevant for PSL)
            0,                         # generation_upload_status
            b'',                       # possible_username_pairs
            now_us,                    # date_last_used
            b'',                       # moving_blocked_for
            now_us,                    # date_password_modified
            '',                        # sender_email
            '',                        # sender_name
            0,                         # date_received
            0,                         # sharing_notification_displayed
            b'',                       # keychain_identifier
            '',                        # sender_profile_image_url
            0,                         # date_last_filled
            0,                         # actor_login_approved
        ))
    conn.commit()
    conn.close()
    print('[setup] done.')


if __name__ == '__main__':
    main()
