from zipfile import ZipFile
import hashlib
from cryptography import x509
from cryptography.hazmat.primitives import serialization

from mitmproxy import certs, ctx
from mitmproxy.options import CONF_BASENAME

import os

# The following 3 variables are for including in the magisk module as text file
MODULE_PROP_TEXT = """id=mitmproxycert
name=MITMProxy cert
version=v1
versionCode=1
author=mitmproxy
description=Adds the mitmproxy certificate to the system store
template=3"""

CONFIG_SH_TEXT = """
MODID=mitmproxycert
AUTOMOUNT=true
PROPFILE=false
POSTFSDATA=false
LATESTARTSERVICE=false

print_modname() {
  ui_print "*******************************"
  ui_print "    MITMProxy cert installer   "
  ui_print "*******************************"
}

REPLACE="
"

set_permissions() {
  set_perm_recursive  $MODPATH  0  0  0755  0644
}
"""

UPDATE_BINARY_TEXT = """
#!/sbin/sh

#################
# Initialization
#################

umask 022

# echo before loading util_functions
ui_print() { echo "$1"; }

require_new_magisk() {
  ui_print "*******************************"
  ui_print " Please install Magisk v20.4+! "
  ui_print "*******************************"
  exit 1
}

OUTFD=$2
ZIPFILE=$3

mount /data 2>/dev/null
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
. /data/adb/magisk/util_functions.sh
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk

install_module
exit 0
"""


def get_ca_from_files() -> x509.Certificate:
    # Borrowed from tlsconfig
    certstore_path = os.path.expanduser(ctx.options.confdir)
    certstore = certs.CertStore.from_store(
        path=certstore_path,
        basename=CONF_BASENAME,
        key_size=ctx.options.key_size,
        passphrase=ctx.options.cert_passphrase.encode("utf8")
        if ctx.options.cert_passphrase
        else None,
    )
    return certstore.default_ca._cert


def subject_hash_old(ca : x509.Certificate) -> str:
    # Mimics the -subject_hash_old option of openssl used for android certificate names
    full_hash = hashlib.md5(ca.subject.public_bytes()).digest()
    sho = (full_hash[0] | (full_hash[1] << 8) | (full_hash[2] << 16) | full_hash[3] << 24)
    return hex(sho)[2:]


def write_magisk_module(path : str):
    # Makes a zip file that can be loaded by Magisk
    # Android certs are stored as DER files
    ca = get_ca_from_files()
    der_cert = ca.public_bytes(serialization.Encoding.DER)
    with ZipFile(path, "w") as zipp:
        # Main cert file, name is always the old subject hash with a '.0' added
        zipp.writestr(f"system/etc/security/cacerts/{subject_hash_old(ca)}.0", der_cert)
        zipp.writestr("module.prop", MODULE_PROP_TEXT)
        zipp.writestr("config.sh", CONFIG_SH_TEXT)
        zipp.writestr("META-INF/com/google/android/updater-script", "#MAGISK")
        zipp.writestr("META-INF/com/google/android/update-binary", UPDATE_BINARY_TEXT)
        zipp.writestr("common/file_contexts_image", "/magisk(/.*)? u:object_r:system_file:s0")
        zipp.writestr("common/post-fs-data.sh", "MODDIR=${0%/*}")
        zipp.writestr("common/service.sh", "MODDIR=${0%/*}")
        zipp.writestr("common/system.prop", "")
