#!/bin/bash

# Sign apk for each target architecture.
# This script does not require command line argument, but it needs 
# some configuration options to be set in set-config.android-signing:
#  - ssh_host_pkgstage is the host which you use for staging packages
#    during signing. The script will download the unsigned .apk files
#    from this host, and upload the signed .apk there
#  - pkgstage_tor_browser_build_dir: this is the path to tor-browser-build
#    on pkgstage
#  - android_signing_key_dir: the local path where the android signing
#    keys are located. That directory should contains files tba_alpha.p12
#    and tba_release.p12 for alpha and release signing keys.
# The Tor Browser version is taken from set-config.tbb-version

set -e
script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
source "$script_dir/functions"
source "$script_dir/set-config.android-signing"

topdir="$script_dir/../.."
ARCHS="armv7 aarch64 x86 x86_64"
projname=$(project-name)

android_signing_key_path="$android_signing_key_dir/tba_$tbb_version_type.p12"
test -f "$android_signing_key_path" || exit_error "$android_signing_key_path is missing"

check_installed_packages() {
  local packages='unzip openjdk-11-jdk-headless openjdk-11-jre-headless'
  for package in $packages
  do
    dpkg -s "$package" | grep -q '^Status: install ok installed$' || \
      exit_error "package $package is missing"
  done
}

setup_build_tools() {
  local rbm="$topdir/rbm/rbm"
  local build_tools_zipfile="$topdir/out/android-toolchain/$("$rbm" showconf --step get_build_tools android-toolchain filename)"
  if ! test -f "$build_tools_zipfile"; then
    "$rbm" build --step get_build_tools android-toolchain
    test -f "$build_tools_zipfile" || exit_error "$build_tools_zipfile is missing"
  fi
  local build_tools_dir=$(mktemp -d)
  trap "rm -Rf $build_tools_dir" EXIT
  unzip -d "$build_tools_dir" "$build_tools_zipfile"
  test -f "$build_tools_dir"/android-12/apksigner || \
    exit_error "$build_tools_dir/android-12/apksigner is missing"
  export PATH="$build_tools_dir/android-12:${PATH}"
}

download_unsigned_apks() {
  apks_dir=$(mktemp -d)
  trap "rm -Rf $apks_dir" EXIT
  rsync -avH "$ssh_host_pkgstage:$pkgstage_tor_browser_build_dir/$SIGNING_PROJECTNAME/$tbb_version_type/signed/$tbb_version/*-qa.apk" "$apks_dir/"
}

upload_signed_apks() {
  rsync -avH --exclude="*-qa.apk" --exclude="*-unaligned.apk" \
    --exclude="*-unsigned.apk" "$apks_dir/" \
    "$ssh_host_pkgstage:$pkgstage_tor_browser_build_dir/$SIGNING_PROJECTNAME/$tbb_version_type/signed/$tbb_version/"
}

# Sign individual apk
sign_apk() {
    INPUTAPK="$1"

    # https://developer.android.com/studio/publish/app-signing#sign-manually
    # After running `gradlew assembleRelease`, creates an unsigned-unaligned apk

    # Aligning ensures that all uncompressed data starts with a particular byte
    # alignment relative to the start of the file, which may reduce the amount
    # of RAM consumed by an app.
    # zipalign -v -p 4 my-app-unsigned.apk my-app-unsigned-aligned.apk
    echo Aligning and signing ${INPUTAPK}

    # Append the different stages of signing
    UNSIGNED_UNALIGNED_APK=`echo "${INPUTAPK}" | sed 's/\.apk/-unsigned-unaligned.apk/'`
    UNSIGNED_APK=`echo "${UNSIGNED_UNALIGNED_APK}" | sed 's/-unaligned//'`
    SIGNED_APK=`echo "${UNSIGNED_APK}" | sed 's/-unsigned//'`

    cp "${INPUTAPK}" "${UNSIGNED_UNALIGNED_APK}"

    # Step 1: Align
    zipalign -v -p 4 "${UNSIGNED_UNALIGNED_APK}" "${UNSIGNED_APK}"
    if [ ! $? = 0 ]; then
        echo "zipalign failed"
        exit 1
    fi
    echo zipalign succeeded

    # Step 2: Verify alignment
    zipalign -vc 4 "${UNSIGNED_APK}"
    if [ ! $? = 0 ]; then
        echo "zipalign verify failed"
        exit 1
    fi
    echo zipalign verify succeeded

    # Step 3: Sign
    # Use this command if reading key from file
    apksigner sign --verbose -ks ${android_signing_key_path} --ks-type pkcs12 --ks-pass env:KSPASS --debuggable-apk-permitted=false --out "${SIGNED_APK}" "${UNSIGNED_APK}"

    # Or, use below command if using a hardware token
    # apksigner sign --verbose --provider-class sun.security.pkcs11.SunPKCS11 --provider-arg pkcs11_java.cfg --ks NONE --ks-type PKCS11 --debuggable-apk-permitted=false --out "${SIGNED_APK}" "${UNSIGNED_APK}"

    if [ ! $? = 0 ]; then
        echo "apksigner sign failed"
        exit 1
    fi
    echo apksigner sign succeeded

    # Step 4: Verify signature
    apksigner verify --verbose "${SIGNED_APK}"
    if [ ! $? = 0 ]; then
        echo "apksigner verify failed"
        exit 1
    fi

    echo apksigner verify succeeded
}

# Rename and verify signing certificate
finalize() {
  for arch in ${ARCHS}; do
      mv ${projname}-${tbb_version}-android-${arch}-multi{-qa,}.apk
  done

  for arch in ${ARCHS}; do
      verified=`apksigner verify --print-certs --verbose ${projname}-${tbb_version}-android-${arch}-multi.apk`
      scheme_v1=
      scheme_v2=
      cert_digest=
      pubkey_digest=

      # Verify the expected signing key was used, Alpha verses Release based on the filename.
      if test "$tbb_version_type" = "alpha"; then
          scheme_v1="Verified using v1 scheme (JAR signing): true"
          scheme_v2="Verified using v2 scheme (APK Signature Scheme v2): true"
          cert_digest="Signer #1 certificate SHA-256 digest: 15f760b41acbe4783e667102c9f67119be2af62fab07763f9d57f01e5e1074e1"
          pubkey_digest="Signer #1 public key SHA-256 digest: 4e617e6516f81123ca58e718d617a704ac8365c575bd9e7a731ba5dd0476869d"
      else
          scheme_v1="Verified using v1 scheme (JAR signing): true"
          scheme_v2="Verified using v2 scheme (APK Signature Scheme v2): true"
          cert_digest="Signer #1 certificate SHA-256 digest: 20061f045e737c67375c17794cfedb436a03cec6bacb7cb9f96642205ca2cec8"
          pubkey_digest="Signer #1 public key SHA-256 digest: 343ca8a2e5452670bdc335a181a4baed909f868937d68c4653e44ef84de8dfc6"
      fi
      for digest in "${scheme_v1}" "${scheme_v2}" "${cert_digest}" "${pubkey_digest}"; do
          if ! `echo "${verified}" | grep -q "${digest}"`; then
              echo "Expected digest not found:"
              echo ${digest}
              echo "in:"
              echo ${verified}
              exit 1
          fi
      done
  done

  echo Done.
}

check_installed_packages

if [ -z "$KSPASS" ]; then
    echo "Enter keystore passphrase"
    stty -echo; read KSPASS; stty echo
    export KSPASS
fi

setup_build_tools

download_unsigned_apks

cd $apks_dir

# Sign all packages
for arch in ${ARCHS}; do
    sign_apk ${projname}-${tbb_version}-android-${arch}-multi-qa.apk
done

finalize

upload_signed_apks
