#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

gpg_bash_lib_function_error_handler() {
   gpg_bash_lib_output_error_handler_last_failed_exit_code="$?"
   gpg_bash_lib_output_error_handler_message="$0 script bug
Please report this bug!
BASH_COMMAND: $BASH_COMMAND
exit_code: $gpg_bash_lib_output_error_handler_last_failed_exit_code"
   gpg_bash_lib_output_failure_status="true"
   gpg_bash_lib_function_diagnostic_message
   gpg_bash_lib_output_error_handler_message="\
$gpg_bash_lib_output_error_handler_message
-----
gpg_bash_lib_output_diagnostic_message:
$gpg_bash_lib_output_diagnostic_message"
   if [ ! "$gpg_bash_lib_input_error_handler_extra" = "" ]; then
      eval $gpg_bash_lib_input_error_handler_extra
   fi
   exit 1
}

gpg_bash_lib_function_extract_trap_cmd() {
   ## Thanks to Richard Hansen - http://stackoverflow.com/a/7287873/2605155
   printf '%s\n' "$3"
}

gpg_bash_lib_function_init() {
   ## Cannot be inside a function, because errtrace might not be set, i.e.
   ## the function may not have inherited the trap, so the existing trap could not
   ## be detected from within a function.
   ## Thanks to Richard Hansen - http://stackoverflow.com/a/7287873/2605155
   gpg_bash_lib_internal_existing_trap="$(eval "gpg_bash_lib_function_extract_trap_cmd $(trap -p ERR)")"

   if test -o pipefail ; then
      true "$FUNCNAME: pipefail is enabled, not enabling it."
      gpg_bash_lib_internal_disable_pipefail="false"
   else
      true "$FUNCNAME: pipefail is disabled, enabling it."
      gpg_bash_lib_internal_disable_pipefail="true"
      set -o pipefail
   fi
   if test -o errtrace ; then
      true "$FUNCNAME: errtrace is enabled, disabling it."
      gpg_bash_lib_internal_enable_errtrace="true"
      set +o errtrace
   else
      true "$FUNCNAME: errtrace is disabled, not enabling it."
      gpg_bash_lib_internal_enable_errtrace="false"
   fi

   ## Not doing this.
   ## Even though we might have disabled errtrace if it was enabled,
   ## it could still be in effect until the end of this function.
   #trap "gpg_bash_lib_function_error_handler" ERR
}

gpg_bash_lib_function_variables() {
   trap "gpg_bash_lib_function_error_handler" ERR

   [ -n "$gpg_bash_lib_input_temp_folder" ] || gpg_bash_lib_input_temp_folder="$(mktemp --directory)"
   gpg_bash_lib_output_failure_status="false"
   gpg_bash_lib_output_diagnostic_message=""
   gpg_bash_lib_internal_gpg_verify_status_fd_file="$gpg_bash_lib_input_temp_folder/gpg_bash_lib_internal_gpg_verify_status_fd_file"
   gpg_bash_lib_internal_gpg_verify_output_file="$gpg_bash_lib_input_temp_folder/gpg_bash_lib_internal_gpg_verify_output_file"
   gpg_bash_lib_output_gpg_import_output=""
   gpg_bash_lib_output_gpg_verify_exit_code=""
   gpg_bash_lib_output_gpg_verify_output=""
   gpg_bash_lib_output_gpg_verify_status_fd_output=""
   gpg_bash_lib_output_signed_on_unixtime=""
   gpg_bash_lib_output_signed_on_date=""
   gpg_bash_lib_output_file_name_tampering=""
   unset gpg_bash_lib_output_notation
   declare -A -g gpg_bash_lib_output_notation
   gpg_bash_lib_output_slow_clock_lenient_up_to_pretty_output=""
   gpg_bash_lib_output_goodsig_status="false"
   gpg_bash_lib_output_validsig_status="false"
   gpg_bash_lib_output_fingerprint_in_hex=""
   gpg_bash_lib_output_current_unixtime=""
   gpg_bash_lib_output_current_time=""
   gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime=""
   gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty=""
   gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime=""
   gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime_pretty=""
   gpg_bash_lib_output_in_future_in_seconds=""
   gpg_bash_lib_output_in_future_pretty_output=""
   gpg_bash_lib_output_freshness_status=""
   gpg_bash_lib_output_freshness_detail=""
   gpg_bash_lib_output_freshness_msg=""
   gpg_bash_lib_output_maximum_age_pretty_output=""
   gpg_bash_lib_output_alright_status=""
   [ -n "$gpg_bash_lib_input_verify_timeout_after" ] || gpg_bash_lib_input_verify_timeout_after="10"
   [ -n "$gpg_bash_lib_input_verify_kill_after" ] || gpg_bash_lib_input_verify_kill_after="10"

   safe-rm --recursive --force -- "$gpg_bash_lib_input_temp_folder"
   mkdir --parents "$gpg_bash_lib_input_temp_folder"
   chmod --recursive 700 "$gpg_bash_lib_input_temp_folder"
   test -d "$gpg_bash_lib_input_temp_folder"
}

gpg_bash_lib_function_sanity_tests() {
   trap "gpg_bash_lib_function_error_handler" ERR
   command -v date >/dev/null
   command -v gpg >/dev/null
   command -v mktemp >/dev/null
}

gpg_bash_lib_function_key_file_to_first_primary_fingerprint() {
   trap "gpg_bash_lib_function_error_handler" ERR

   ## example command:
   ##
   ## gpg --batch --no-options --homedir /home/user/.cache/tb/gpgtmpdir --with-colons --fixed-list-mode --with-fingerprint /usr/share/torbrowser-updater-keys.d/tbb-team.asc
   ##
   ## example output:
   ##
   ## gpg: WARNING: no command supplied.  Trying to guess what you mean ...
   ## ...
   ##
   ## Therefore using '--import-options show-only'.

   ## example command:
   ##
   ## gpg --batch --no-options --homedir /home/user/.cache/tb/gpgtmpdir --with-colons --fixed-list-mode --with-fingerprint --import --import-options show-only /usr/share/torbrowser-updater-keys.d/tbb-team.asc
   ##
   ## example output:
   ##
   ## gpg: key 4E2C6E8793298290: 1 duplicate signature removed
   ## gpg: key 4E2C6E8793298290: 236 signatures not checked due to missing keys
   ## gpg: key 4E2C6E8793298290: 1 signature reordered
   ## ...
   ##
   ## Therefore using '--import-options show-only,import-minimal'.

   ## example command:
   ##
   ## gpg --batch --no-options --homedir /home/user/.cache/tb/gpgtmpdir --with-colons --fixed-list-mode --with-fingerprint --import --import-options show-only,import-minimal --trust-model direct /usr/share/torbrowser-updater-keys.d/tbb-team.asc
   ##
   ## example output:
   ##
   ## pub:u:4096:1:4E2C6E8793298290:1418637242:1598268349::u:::cSC::::::23::0:
   ## fpr:::::::::EF6E286DDA85EA2A4BA7DE684E2C6E8793298290:
   ## uid:u::::1440588357::50CE13CA9C3B3D27C0071489BB58D4B898131885::Tor Browser Developers (signing key) <torbrowser@torproject.org>::::::::::0:
   ## sub:e:4096:1:7017ADCEF65C2036:1418637307:1503660203:::::s::::::23:
   ## fpr:::::::::5242013F02AFC851B1C736B87017ADCEF65C2036:
   ## sub:e:4096:1:2E1AC68ED40814E0:1418637398:1503660390:::::s::::::23:
   ## fpr:::::::::BA1EE421BBB45263180E1FC72E1AC68ED40814E0:
   ## sub:r:4096:1:2D000988589839A3:1418637546::::::s::::::23:
   ## fpr:::::::::05FA44253F6C19A8B7F518D42D000988589839A3:
   ## sub:e:4096:1:D1483FA6C3C07136:1472037984:1535109984:::::s::::::23:
   ## fpr:::::::::A4300A6BC93C0877A4451486D1483FA6C3C07136:
   ## sub:u:4096:1:EB774491D9FF06E2:1527369844:1599945844:::::s::::::23:
   ## fpr:::::::::110775B5D101FB36BC6C911BEB774491D9FF06E2:

   local gpg_bash_lib_internal_key_fingerprint_output gpg_bash_lib_internal_first_three
   local gpg_bash_lib_internal_fingerprint_without_collons gpg_bash_lib_internal_key_file_path
   local gpg_bash_lib_internal_fingerprint_final
   gpg_bash_lib_internal_key_file_path="$1"
   gpg_bash_lib_internal_key_fingerprint_output="$( \
      timeout \
         --kill-after="$gpg_bash_lib_input_verify_kill_after" "$gpg_bash_lib_input_verify_timeout_after" \
            gpg \
               --batch \
               --no-options \
               --homedir "$gpg_bash_lib_input_temp_folder" \
               --with-colons \
               --fixed-list-mode \
               --with-fingerprint \
               --import-options show-only,import-minimal \
               --import \
               "$gpg_bash_lib_internal_key_file_path" \
   )"
   while read -r -d $'\n' gpg_bash_lib_internal_line; do
      gpg_bash_lib_internal_first_three="${gpg_bash_lib_internal_line:0:3}"
      if [ ! "$gpg_bash_lib_internal_first_three" = "fpr" ]; then
         continue
      fi
      ## Example gpg_bash_lib_internal_line:
      ## fpr:::::::::916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA:
      gpg_bash_lib_internal_fingerprint_without_collons="${gpg_bash_lib_internal_line//:/}"
      ## Example gpg_bash_lib_internal_fingerprint_without_collons:
      ## fpr916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA
      gpg_bash_lib_internal_fingerprint_final="${gpg_bash_lib_internal_fingerprint_without_collons/fpr/}"
      ## Example gpg_bash_lib_internal_fingerprint_final:
      ## 916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA
      break
   done < <( echo "$gpg_bash_lib_internal_key_fingerprint_output" )
   echo "$gpg_bash_lib_internal_fingerprint_final"
}

gpg_bash_lib_function_alright_maybe() {
   trap "gpg_bash_lib_function_error_handler" ERR
   if [ "$gpg_bash_lib_output_alright_status" = "false" ]; then
      return 0
   fi
   if [ ! "$gpg_bash_lib_output_goodsig_status" = "true" ]; then
     gpg_bash_lib_output_alright_status="false"
     return 0
   fi
   if [ ! "$gpg_bash_lib_output_validsig_status" = "true" ]; then
     gpg_bash_lib_output_alright_status="false"
     return 0
   fi
   gpg_bash_lib_output_alright_status="true"
}

gpg_bash_lib_function_import_keys_to_temp_dir() {
   trap "gpg_bash_lib_function_error_handler" ERR

   test -d "$gpg_bash_lib_input_key_import_dir"

   if shopt -q nullglob ; then
      gpg_bash_lib_internal_disable_nullglob="false"
   else
      gpg_bash_lib_internal_disable_nullglob="true"
      shopt -s nullglob
   fi
   if shopt -q dotglob ; then
      gpg_bash_lib_internal_disable_dotglob="false"
   else
      gpg_bash_lib_internal_disable_dotglob="true"
      shopt -s dotglob
   fi

   local gpg_bash_lib_internal_key

   for gpg_bash_lib_internal_key in "$gpg_bash_lib_input_key_import_dir/"*; do
      local gpg_bash_lib_internal_key_file_name
      gpg_bash_lib_internal_key_file_name="$(basename "$gpg_bash_lib_internal_key")"
      if [ "$gpg_bash_lib_internal_key_file_name" = "placeholder" ]; then
         continue
      fi
      ## Using --ignore-time-conflict, because otherwise
      ## check "gpg --import"'s exit code would exit with a non-zero return code
      ## if key import failed (such as code 1, see example below).
      ## Example:
      ##     gpg: key 63FEE659 was created 119604046 seconds in the future (time warp or clock problem)
      ## Therefore we ignore time conflicts, because a clock sanity check will be done later.
      gpg_bash_lib_output_gpg_import_output="$( \
            gpg \
               --batch \
               --no-options \
               --ignore-time-conflict \
               --homedir "$gpg_bash_lib_input_temp_folder" \
               --import "$gpg_bash_lib_internal_key" \
               2>&1 \
         )"
      gpg_bash_lib_internal_primary_key_fingerprint="$(gpg_bash_lib_function_key_file_to_first_primary_fingerprint "$gpg_bash_lib_internal_key")"
      echo "$gpg_bash_lib_internal_primary_key_fingerprint:6:" | \
         gpg \
               --batch \
               --no-options \
               --ignore-time-conflict \
               --homedir "$gpg_bash_lib_input_temp_folder" \
               --import-ownertrust \
               1>/dev/null \
               2>/dev/null
   done

   if [ "$gpg_bash_lib_internal_disable_nullglob" = "true" ]; then
      shopt -u nullglob
   fi
   if [ "$gpg_bash_lib_internal_disable_dotglob" = "true" ]; then
      shopt -u dotglob
   fi
}

gpg_bash_lib_function_verify() {
   trap "gpg_bash_lib_function_error_handler" ERR

   safe-rm --force -- "$gpg_bash_lib_internal_gpg_verify_status_fd_file"

   test -f "$gpg_bash_lib_input_data_file"
   test -f "$gpg_bash_lib_input_sig_file"

   ## Up to 30 minutes lenient about slow clocks by default.
   [ -n "$gpg_bash_lib_input_slow_clock_lenient_up_to_seconds" ] || gpg_bash_lib_input_slow_clock_lenient_up_to_seconds="$(( 30 * 60 ))"
   gpg_bash_lib_output_slow_clock_lenient_up_to_pretty_output="$(gpg_bash_lib_function_displaytime "$gpg_bash_lib_input_slow_clock_lenient_up_to_seconds")"

   ## One month has 2592000 seconds.
   ## (60 [seconds] * 60 [minutes] * 24 [hours] * 30 [days])
   gpg_bash_lib_internal_month_has_seconds="2592000"
   [ -n "$gpg_bash_lib_input_maximum_age_in_seconds" ] || gpg_bash_lib_input_maximum_age_in_seconds="$gpg_bash_lib_internal_month_has_seconds"
   gpg_bash_lib_output_maximum_age_pretty_output="$(gpg_bash_lib_function_displaytime "$gpg_bash_lib_input_maximum_age_in_seconds")"

   ## {{ Dry Run

   ## Dry run of gpg --fingerprint for more pretty
   ## gpg_bash_lib_output_gpg_verify_output that does not contain the trustdb
   ## related messages on first run of gpg.

   local gpg_bash_lib_internal_gpg_fingerprint_pid

   timeout \
      --kill-after="$gpg_bash_lib_input_verify_kill_after" "$gpg_bash_lib_input_verify_timeout_after" \
         gpg \
            --batch \
            --no-options \
            --homedir "$gpg_bash_lib_input_temp_folder" \
            --fingerprint \
            >/dev/null \
            2>&1 \
            &

   gpg_bash_lib_internal_gpg_fingerprint_pid="$!"
   if wait "$gpg_bash_lib_internal_gpg_fingerprint_pid" ; then
      gpg_bash_lib_output_gpg_fingerprint_exit_code="0"
   else
      gpg_bash_lib_output_gpg_fingerprint_exit_code="$?"
   fi

   if [ ! "$gpg_bash_lib_output_gpg_fingerprint_exit_code" = "0" ]; then
      error "Dry run of 'gpg --fingerprint' failed!"
   fi

   ## }}

   local gpg_bash_lib_internal_gpg_verify_pid

   timeout \
      --kill-after="$gpg_bash_lib_input_verify_kill_after" "$gpg_bash_lib_input_verify_timeout_after" \
         gpg \
            --batch \
            --no-options \
            --ignore-time-conflict \
            --with-fingerprint \
            --status-file "$gpg_bash_lib_internal_gpg_verify_status_fd_file" \
            --homedir "$gpg_bash_lib_input_temp_folder" \
            --verify-options show-notations \
            --verify "$gpg_bash_lib_input_sig_file" \
            "$gpg_bash_lib_input_data_file" \
            > "$gpg_bash_lib_internal_gpg_verify_output_file" \
            2>&1 \
            &

   gpg_bash_lib_internal_gpg_verify_pid="$!"
   if wait "$gpg_bash_lib_internal_gpg_verify_pid" ; then
      gpg_bash_lib_output_gpg_verify_exit_code="0"
   else
      gpg_bash_lib_output_gpg_verify_exit_code="$?"
   fi

   ## `timeout` returns:
   ## - 124 if sigterm was sufficient
   ## - 137 if needed to use kill.

   gpg_bash_lib_output_gpg_verify_output="$(cat "$gpg_bash_lib_internal_gpg_verify_output_file")" || true
}

gpg_bash_lib_function_parse_status_file() {
   trap "gpg_bash_lib_function_error_handler" ERR

   test -f "$gpg_bash_lib_internal_gpg_verify_status_fd_file"

   gpg_bash_lib_output_gpg_verify_status_fd_output="$(cat "$gpg_bash_lib_internal_gpg_verify_status_fd_file")"

   ## See also: /usr/share/doc/gnupg/DETAILS.gz

   ## Example gpg_bash_lib_internal_gpg_verify_status_fd_file:
   ## [GNUPG:] SIG_ID xl52dwvbkcu7YkXw6Zv/PUhexUk 2015-01-15 1421344751
   ## [GNUPG:] GOODSIG CB8D50BB77BB3C48 Patrick Schleizer <adrelanos@whonix.org>
   ## [GNUPG:] NOTATION_NAME issuer-fpr@notations.openpgp.fifthhorseman.net
   ## [GNUPG:] NOTATION_DATA 6E979B28A6F37C43BE30AFA1CB8D50BB77BB3C48
   ## [GNUPG:] NOTATION_NAME file@name
   ## [GNUPG:] NOTATION_DATA a
   ## [GNUPG:] VALIDSIG 6E979B28A6F37C43BE30AFA1CB8D50BB77BB3C48 2015-01-15 1421344751 0 4 0 1 10 00 916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA
   ## [GNUPG:] TRUST_ULTIMATE

   ## Another gpg_bash_lib_internal_gpg_verify_status_fd_file::
   ## [GNUPG:] ERRSIG 9C131AD3713AAEEF 1 10 00 1381635738 9
   ## [GNUPG:] NO_PUBKEY 9C131AD3713AAEEF

   ## Another gpg_bash_lib_internal_gpg_verify_status_fd_file::
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] SIG_ID Tk/VeQa7RTYC/8JwlICrAFEi5es 2015-01-27 1422383418
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] EXPKEYSIG 8D6648AA636E1951 auto generated test key for unit test <unit@test.key>
   ## [GNUPG:] NOTATION_NAME file@name
   ## [GNUPG:] NOTATION_DATA test-file
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] KEYEXPIRED 1607731200
   ## [GNUPG:] SIGEXPIRED deprecated-use-keyexpired-instead
   ## [GNUPG:] VALIDSIG 8A2EEB750FD937F70828D5628D6648AA636E1951 2015-01-27 1422383418 0 4 0 1 2 00 8BC6714D5654D0813BBAB42F818A9682FE742EFD'

   ## KEYEXPIRED -> not useful as per gnupg documentation DETAILS file
   ## SIGEXPIRED -> deprecated as per gnupg documentation DETAILS file

   ## Sanity test.
   if grep -i "BADSIG\|EXPSIG\|EXPKEYSIG\|REVKEYSIG\|ERRSIG\|NODATA\|UNEXPECTED\|NO_PUBKEY\|NO_SECKEY\|ERROR" "$gpg_bash_lib_internal_gpg_verify_status_fd_file" >/dev/null 2>/dev/null; then
      gpg_bash_lib_output_validsig_status="false"
      gpg_bash_lib_output_alright_status="false"
      return 0
   fi

   local gpg_bash_lib_internal_line gpg_bash_lib_internal_trust_ultimate
   gpg_bash_lib_internal_trust_ultimate="false"
   while read -r -d $'\n' gpg_bash_lib_internal_line; do
      true "gpg_bash_lib_internal_line: $gpg_bash_lib_internal_line"
      local one two three four five
      read -r one two three four five _ <<< "$gpg_bash_lib_internal_line"
      true "one: $one | two: $two | three: $three | four: $four | five: $five"
      if [ ! "$one" = "[GNUPG:]" ]; then
         ## XXX: Are there cases where gnupg exits 0, but status messages still
         ##      do not start with [GNUPG:]?
         error "gpg_bash_lib_internal_line did not start with '[GNUPG:]'!"
      fi
      if [ "$two" = "GOODSIG" ]; then
         gpg_bash_lib_output_goodsig_status="true"
      fi
      if [ "$two" = "VALIDSIG" ]; then
         gpg_bash_lib_output_validsig_status="true"
         gpg_bash_lib_output_fingerprint_in_hex="$three"
         gpg_bash_lib_output_signed_on_unixtime="$five"
      fi
      if [ "$two" = "NOTATION_NAME" ]; then
         gpg_bash_lib_internal_notation_name="$three"
      fi
      if [ "$two" = "NOTATION_DATA" ]; then
         gpg_bash_lib_output_notation[$gpg_bash_lib_internal_notation_name]="$three"
      fi
      if [ "$two" = "TRUST_ULTIMATE" ]; then
         gpg_bash_lib_internal_trust_ultimate="true"
      fi
      if printf '%s\n' "$two" | grep -i "TRUST_" >/dev/null 2>/dev/null; then
         ## Stop parsing after first signature. Multiple signatures within a
         ## a single file are not yet implemented. And would be difficult to
         ## implement, because gpg does not provide a separator for multiple
         ## signatures, see:
         ## https://lists.gnupg.org/pipermail/gnupg-users/2015-March/053253.html
         break
      fi
   done < "$gpg_bash_lib_internal_gpg_verify_status_fd_file"
   unset gpg_bash_lib_internal_line

   ## Sanity test.
   if [ "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      if [ ! "$gpg_bash_lib_internal_trust_ultimate" = "true" ]; then
         error "gpg_bash_lib_internal_trust_ultimate not set to 'true'!"
      fi
   fi

   if [ "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      if [ "$gpg_bash_lib_output_signed_on_unixtime" = "" ]; then
         error "gpg_bash_lib_output_signed_on_unixtime is empty!"
      fi
   fi

   if [ "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      ## Thanks to:
      ## http://mywiki.wooledge.org/BashFAQ/054
      if [[ "$gpg_bash_lib_output_signed_on_unixtime" != *[!0-9]* ]]; then
         true "'$gpg_bash_lib_output_signed_on_unixtime' is strictly numeric."
      else
         error "gpg_bash_lib_output_signed_on_unixtime is NOT strictly numeric, it is: $gpg_bash_lib_output_signed_on_unixtime"
      fi
   fi

   if [ "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      gpg_bash_lib_output_signed_on_date="$(date --utc --date "@$gpg_bash_lib_output_signed_on_unixtime" "+%B %d %H:%M:%S UTC %Y")"
   fi

   if [ "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      if [ "${gpg_bash_lib_output_notation["file@name"]}" = "" ]; then
         true "\${gpg_bash_lib_output_notation["file@name"]} is unset."
         gpg_bash_lib_output_file_name_tampering="missing"
         if [ "$gpg_bash_lib_input_file_name_enforce" = "true" ]; then
            gpg_bash_lib_output_alright_status="false"
         fi
      else
         true "\${gpg_bash_lib_output_notation["file@name"]} is set to: ${gpg_bash_lib_output_notation["file@name"]}"
         if [ "$(basename "$gpg_bash_lib_input_data_file")" = "${gpg_bash_lib_output_notation["file@name"]}" ]; then
            gpg_bash_lib_output_file_name_tampering="false"
         else
            gpg_bash_lib_output_file_name_tampering="true"
            if [ "$gpg_bash_lib_input_file_name_enforce" = "true" ]; then
               gpg_bash_lib_output_alright_status="false"
            fi
         fi
      fi
   else
      gpg_bash_lib_output_file_name_tampering="missing"
      gpg_bash_lib_output_alright_status="false"
   fi
}

gpg_bash_lib_function_process() {
   trap "gpg_bash_lib_function_error_handler" ERR

   gpg_bash_lib_output_current_unixtime="$(date +%s)"
   gpg_bash_lib_output_current_time="$(date --utc "+%B %d %H:%M:%S UTC %Y")"

   gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime="$(( $gpg_bash_lib_output_signed_on_unixtime - $gpg_bash_lib_output_current_unixtime ))"
   gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty="$(gpg_bash_lib_function_displaytime "$gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime")"

   gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime="$(( $gpg_bash_lib_output_current_unixtime - $gpg_bash_lib_output_signed_on_unixtime ))"
   gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime_pretty="$(gpg_bash_lib_function_displaytime "$gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime")"

   gpg_bash_lib_output_in_future_in_seconds="$(( $gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime - $gpg_bash_lib_input_maximum_age_in_seconds ))"
   gpg_bash_lib_output_in_future_pretty_output="$(gpg_bash_lib_function_displaytime "$gpg_bash_lib_output_in_future_in_seconds")"

   if [ "$gpg_bash_lib_output_current_unixtime" -le "$gpg_bash_lib_output_signed_on_unixtime" ]; then
      if [ "$gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime" -lt "$gpg_bash_lib_input_slow_clock_lenient_up_to_seconds" ]; then
         gpg_bash_lib_output_freshness_status="true"
         gpg_bash_lib_output_freshness_detail="lenient"
         gpg_bash_lib_output_freshness_msg="\
- Freshness: Signature is not yet valid.
- valid-max: Signatures are valid up to $gpg_bash_lib_output_maximum_age_pretty_output.
- Signature Creation Date: $gpg_bash_lib_output_signed_on_date
- Current System Date    : $gpg_bash_lib_output_current_time
- Local System Clock: Your clock might be slow. (Could be at least $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty slow.)
- Relative Signature Creation Time: According to your system clock, signature was created $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty before current time.
- Leniency: Ignoring this, because it still is within range.
Valid up to gpg_bash_lib_output_slow_clock_lenient_up_to_pretty_output $gpg_bash_lib_output_slow_clock_lenient_up_to_pretty_output before."
         gpg_bash_lib_function_alright_maybe
         return 0
      else
         gpg_bash_lib_output_freshness_status="false"
         gpg_bash_lib_output_freshness_detail="slow"
         gpg_bash_lib_output_freshness_msg="\
- Freshness: Signature is not yet valid.
- valid-max: Signatures are valid up to $gpg_bash_lib_output_maximum_age_pretty_output.
- Signature Creation Date: $gpg_bash_lib_output_signed_on_date
- Current System Date    : $gpg_bash_lib_output_current_time
- Local System Clock: Your clock might be slow. (Could be at least $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty slow.)
- Relative Signature Creation Time: According to your system clock, signature was created $gpg_bash_lib_output_signed_on_unixtime_minus_current_unixtime_pretty before current time."
         gpg_bash_lib_output_alright_status="false"
         return 0
      fi
   elif [ "$gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime" -ge "$gpg_bash_lib_input_maximum_age_in_seconds" ]; then
      gpg_bash_lib_output_freshness_status="false"
      gpg_bash_lib_output_freshness_detail="outdated"
      gpg_bash_lib_output_freshness_msg="\
- Freshness: Signature is no longer valid (outdated).
- valid-max: Signatures are valid up to $gpg_bash_lib_output_maximum_age_pretty_output.
- Signature Creation Date: $gpg_bash_lib_output_signed_on_date
- Current System Date    : $gpg_bash_lib_output_current_time
- Local System Clock: Your clock might be fast. (Could be at least $gpg_bash_lib_output_in_future_pretty_output fast.)
- Relative Signature Creation Time: According to your system clock, signature was created $gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime_pretty ago.
- Extra: It might have been forgotten to refresh the signature (invalid since $gpg_bash_lib_output_in_future_pretty_output)."
      gpg_bash_lib_output_alright_status="false"
      return 0
   else
      gpg_bash_lib_output_freshness_status="true"
      gpg_bash_lib_output_freshness_detail="current"
      gpg_bash_lib_output_freshness_msg="\
- Freshness: Signature is current.
- valid-max: Signatures are valid up to $gpg_bash_lib_output_maximum_age_pretty_output.
- Signature Creation Date: $gpg_bash_lib_output_signed_on_date
- Current System Date    : $gpg_bash_lib_output_current_time
- Local System Clock: Your clock seems okay.
- Relative Signature Creation Time: According to your system clock, signature was created $gpg_bash_lib_output_current_unixtime_minus_signed_on_unixtime_pretty ago."
      gpg_bash_lib_function_alright_maybe
      return 0
   fi
}

gpg_bash_lib_function_diagnostic_message() {
   trap "gpg_bash_lib_function_error_handler" ERR

   gpg_bash_lib_output_diagnostic_message="\
gpg_bash_lib_internal_gpg_verify_status_fd_file: $gpg_bash_lib_internal_gpg_verify_status_fd_file
gpg_bash_lib_internal_gpg_verify_output_file: $gpg_bash_lib_internal_gpg_verify_output_file
gpg_bash_lib_output_gpg_import_output:
$gpg_bash_lib_output_gpg_import_output
gpg_bash_lib_output_gpg_verify_output:
$gpg_bash_lib_output_gpg_verify_output
gpg_bash_lib_output_gpg_verify_status_fd_output:
$gpg_bash_lib_output_gpg_verify_status_fd_output"
}

gpg_bash_lib_function_cleanup() {
   trap "gpg_bash_lib_function_error_handler" ERR

   if [ "$gpg_bash_lib_input_cleanup" = "true" ]; then
      if [ -e "$gpg_bash_lib_internal_gpg_verify_status_fd_file" ]; then
         safe-rm --force -- "$gpg_bash_lib_internal_gpg_verify_status_fd_file"
      fi
      if [ -d "$gpg_bash_lib_input_temp_folder" ]; then
         safe-rm --force --recursive -- "$gpg_bash_lib_input_temp_folder"
      fi
   fi
}

gpg_bash_lib_function_deinit() {
   trap "gpg_bash_lib_function_error_handler" ERR

   if [ "$gpg_bash_lib_internal_disable_pipefail" = "true" ]; then
      true "$FUNCNAME: pipefail has been enabled, disabling it for deinit."
      set +o pipefail
   else
      true "$FUNCNAME: pipefail was enabled, leaving it as is for deinit."
   fi

   if [ "$gpg_bash_lib_internal_enable_errtrace" = "true" ]; then
      true "$FUNCNAME: errtrace has been disabled, enabling it for deinit."
      set -o errtrace
   else
      true "$FUNCNAME: errtrace was disabled, leaving it as is for deinit."
   fi

   true "$FUNCNAME: restoring previously existing trap."
   trap "$gpg_bash_lib_internal_existing_trap" ERR
}

gpg_bash_lib_function_end() {
   gpg_bash_lib_function_diagnostic_message
   gpg_bash_lib_function_cleanup
   gpg_bash_lib_function_deinit
}

gpg_bash_lib_function_main_verify() {
   gpg_bash_lib_function_init
   gpg_bash_lib_function_sanity_tests
   gpg_bash_lib_function_variables
   gpg_bash_lib_function_import_keys_to_temp_dir
   gpg_bash_lib_function_verify
   if [ ! "$gpg_bash_lib_output_gpg_verify_exit_code" = "0" ]; then
      gpg_bash_lib_function_end
      return 0
   fi
   gpg_bash_lib_function_parse_status_file
   if [ ! "$gpg_bash_lib_output_validsig_status" = "true" ]; then
      gpg_bash_lib_function_end
      return 0
   fi
   gpg_bash_lib_function_process
   gpg_bash_lib_function_end
}
