#!/bin/bash
#
# Validate an HTTPS endpoint against Apple's ATS and FCP_v2.1 diagnostics.
# Run this on macOS 26.4 or later for the FCP_v2.1 result Apple references.

set -u

usage() {
    printf 'Usage: %s <domain_or_url>\n' "$0"
    printf 'Examples:\n'
    printf '  %s fw.example.org\n' "$0"
    printf '  %s https://fw.example.org\n' "$0"
    printf '  %s fw.example.org:8443\n' "$0"
}

die() {
    printf 'Error: %s\n' "$1" >&2
    exit 1
}

trim() {
    awk '{$1=$1; print}'
}

if [ "$#" -ne 1 ]; then
    usage
    exit 1
fi

if [ ! -x /usr/bin/nscurl ]; then
    die "This script must be run on macOS with /usr/bin/nscurl available."
fi

if ! command -v openssl >/dev/null 2>&1; then
    die "openssl was not found in PATH."
fi

INPUT_TARGET="$1"
TARGET="$INPUT_TARGET"

case "$TARGET" in
    http://*)
        TARGET="${TARGET#http://}"
        ;;
    https://*)
        TARGET="${TARGET#https://}"
        ;;
    *://*)
        die "Only HTTP or HTTPS URLs are supported. Provide a DNS name or HTTPS URL."
        ;;
esac

TARGET="${TARGET%%/*}"
TARGET="${TARGET%%\?*}"
TARGET="${TARGET%%#*}"
TARGET="${TARGET##*@}"
TARGET="${TARGET%.}"

if [ -z "$TARGET" ]; then
    die "Could not determine a host from: $INPUT_TARGET"
fi

case "$TARGET" in
    *'['*|*']'*)
        die "IPv6 literals are not supported by this helper. Use the server DNS name."
        ;;
esac

HOST="$TARGET"
PORT="443"

case "$TARGET" in
    *:*)
        HOST="${TARGET%%:*}"
        PORT="${TARGET##*:}"
        ;;
esac

if [ -z "$HOST" ]; then
    die "Host is empty after parsing: $INPUT_TARGET"
fi

case "$PORT" in
    ''|*[!0-9]*)
        die "Port must be numeric: $PORT"
        ;;
esac

URL="https://$HOST"
if [ "$PORT" != "443" ]; then
    URL="$URL:$PORT"
fi
URL="$URL/"
CONNECT_TARGET="$HOST:$PORT"

extract_nscurl_result() {
    label="$1"
    printf '%s\n' "$NSCURL_OUT" | awk -v wanted="$label" '
        BEGIN {
            wanted = tolower(wanted)
            in_block = 0
        }
        {
            line = tolower($0)
            if (index(line, wanted) > 0) {
                in_block = 1
                next
            }
            if (in_block && $0 ~ /Result[[:space:]]*:/) {
                print $NF
                exit
            }
            if (in_block && $0 ~ /^={10,}/) {
                in_block = 0
            }
        }
    '
}

print_result_line() {
    label="$1"
    result="$2"

    case "$result" in
        PASS)
            printf '  %-18s PASS\n' "$label:"
            ;;
        FAIL)
            printf '  %-18s FAIL\n' "$label:"
            ;;
        *)
            printf '  %-18s UNKNOWN\n' "$label:"
            ;;
    esac
}

extract_ssl_field() {
    field="$1"
    printf '%s\n' "$OPENSSL_OUT" | awk -F: -v field="$field" '
        {
            key = $1
            gsub(/^[ \t]+|[ \t]+$/, "", key)
        }
        key == field {
            value = $2
            gsub(/^[ \t]+|[ \t]+$/, "", value)
            print value
            exit
        }
    '
}

printf '==========================================================\n'
printf 'Validating Apple ATS and FCP_v2.1 readiness for %s\n' "$CONNECT_TARGET"
printf '==========================================================\n'

printf '\n[1/3] Running Apple nscurl ATS diagnostics...\n'
NSCURL_OUT="$(/usr/bin/nscurl --ats-diagnostics "$URL" 2>&1)"
NSCURL_STATUS=$?

if [ "$NSCURL_STATUS" -ne 0 ]; then
    printf '  nscurl exited with status %s. Raw diagnostic output follows.\n' "$NSCURL_STATUS"
    printf '%s\n' "$NSCURL_OUT"
    exit 1
fi

DEFAULT_RESULT="$(extract_nscurl_result "Default ATS Secure Connection")"
FCP_RESULT="$(extract_nscurl_result "FCP_v2.1")"

print_result_line "Standard ATS" "$DEFAULT_RESULT"
print_result_line "FCP_v2.1" "$FCP_RESULT"

printf '\n[2/3] Checking negotiated TLS protocol and cipher with OpenSSL...\n'
OPENSSL_OUT="$(openssl s_client -connect "$CONNECT_TARGET" -servername "$HOST" </dev/null 2>/dev/null)"

TLS_PROTOCOL="$(extract_ssl_field "Protocol" | trim)"
TLS_CIPHER="$(extract_ssl_field "Cipher" | trim)"
VERIFY_RESULT="$(printf '%s\n' "$OPENSSL_OUT" | awk '/Verify return code:/ {sub(/^[ \t]*Verify return code:[ \t]*/, ""); print; exit}')"
TEMP_KEY="$(printf '%s\n' "$OPENSSL_OUT" | awk -F': ' '/Server Temp Key:/ {print $2; exit}')"

if [ -n "$TLS_PROTOCOL" ]; then
    printf '  Protocol: %s\n' "$TLS_PROTOCOL"
else
    printf '  Protocol: UNKNOWN\n'
fi

if [ -n "$TLS_CIPHER" ]; then
    printf '  Cipher:   %s\n' "$TLS_CIPHER"
else
    printf '  Cipher:   UNKNOWN\n'
fi

if [ -n "$TEMP_KEY" ]; then
    printf '  Temp key: %s\n' "$TEMP_KEY"
fi

case "$TLS_PROTOCOL:$TLS_CIPHER" in
    TLSv1.3:*)
        printf '  PFS:      yes, TLS 1.3 ciphers provide forward secrecy\n'
        ;;
    *:ECDHE*)
        printf '  PFS:      yes, ECDHE was negotiated\n'
        ;;
    *)
        printf '  PFS:      review needed; OpenSSL did not show TLS 1.3 or ECDHE\n'
        ;;
esac

if [ -n "$VERIFY_RESULT" ]; then
    printf '  Verify:   %s\n' "$VERIFY_RESULT"
fi

printf '\n[3/3] Checking certificate signature details...\n'
CERT_TEXT="$(printf '%s\n' "$OPENSSL_OUT" | openssl x509 -text -noout 2>/dev/null)"
SIG_ALG="$(printf '%s\n' "$CERT_TEXT" | awk -F': ' '/Signature Algorithm/ {print $2; exit}' | trim)"
PUBLIC_KEY="$(printf '%s\n' "$CERT_TEXT" | awk -F'[()]' '/Public-Key:/ {print $2; exit}' | trim)"

if [ -n "$SIG_ALG" ]; then
    printf '  Signature algorithm: %s\n' "$SIG_ALG"
    case "$(printf '%s' "$SIG_ALG" | tr '[:upper:]' '[:lower:]')" in
        *sha1*|*md5*)
            printf '  Signature status:    review needed; weak hash algorithm detected\n'
            ;;
        *)
            printf '  Signature status:    no SHA-1 or MD5 signature detected\n'
            ;;
    esac
else
    printf '  Signature algorithm: UNKNOWN\n'
fi

if [ -n "$PUBLIC_KEY" ]; then
    printf '  Public key size:     %s\n' "$PUBLIC_KEY"
fi

printf '\n==========================================================\n'

EXIT_CODE=0
if [ "$DEFAULT_RESULT" = "FAIL" ] || [ "$FCP_RESULT" = "FAIL" ]; then
    printf 'RESULT: Remediation required before this endpoint can be treated as ready for Apple stricter TLS requirements.\n'
    EXIT_CODE=2
elif [ "$DEFAULT_RESULT" = "PASS" ] && [ "$FCP_RESULT" = "PASS" ]; then
    printf 'RESULT: nscurl reports this endpoint passes Standard ATS and FCP_v2.1.\n'
else
    printf 'RESULT: Unable to parse a complete nscurl ATS/FCP result. Run the raw nscurl command and review the output manually.\n'
    EXIT_CODE=3
fi

printf 'Raw Apple test command: /usr/bin/nscurl --ats-diagnostics %s\n' "$URL"
exit "$EXIT_CODE"
