#!/bin/bash
# =============================================================================
# decrypt-run.sh — Decrypt-and-Execute Wrapper
# =============================================================================
# Decrypts an .enc tool file to /dev/shm, executes it, then shreds plaintext.
# AES key fetched from Vault (869377) via compass tunnel on first use, cached
# in memory for session. HMAC verified before decryption.
#
# Usage: decrypt-run.sh <tool_file.enc> [args...]
# Env:   NODE_ID, TOOL_DIR, VAULT_HOST (defaults provided)
# =============================================================================
set -euo pipefail

NODE_ID="${NODE_ID:-$(cat /etc/binary/node_id 2>/dev/null || hostname -s)}"
# Resolve TOOL_DIR from engine.json
_ej_dr="${SCRIPT_DIR:-$(dirname "$0")}/engine.json"
if [[ -f "${_ej_dr}" ]] && command -v python3 &>/dev/null; then
    TOOL_DIR="${TOOL_DIR:-$(python3 -c "import json; print(json.load(open('${_ej_dr}'))['tool_dir'])" 2>/dev/null || echo "/opt/binary/tools")}"
else
    TOOL_DIR="${TOOL_DIR:-/opt/binary/tools}"
fi
VAULT_HOST="${VAULT_HOST:-10.69.69.8}"
VAULT_PORT="${VAULT_PORT:-9922}"
VAULT_USER="${VAULT_USER:-admin}"
VAULT_KEY="${VAULT_KEY:-/home/admin/.ssh/gabriella_key}"
SHM_DIR="/dev/shm/.t_${NODE_ID}"
KEY_CACHE="/dev/shm/.k_${NODE_ID}"

# ── Ensure SHM directory exists with strict perms ──
mkdir -p "$SHM_DIR" 2>/dev/null
chmod 700 "$SHM_DIR" 2>/dev/null

# ── Fetch AES key from Vault (cached in /dev/shm for session) ──
fetch_key() {
    local key_type="$1"  # "aes" or "hmac"
    local cache_file="${KEY_CACHE}.${key_type}"
    
    # Return cached key if exists and fresh (< 5 min old)
    if [[ -f "$cache_file" ]]; then
        local age
        age=$(( $(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0) ))
        if [[ $age -lt 300 ]]; then
            cat "$cache_file"
            return 0
        fi
    fi
    
    # Fetch from Vault via SSH
    local remote_path="/var/vault/keys/${NODE_ID}.${key_type}"
    local key_data
    key_data=$(ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=8 \
        -i "$VAULT_KEY" -p "$VAULT_PORT" \
        "${VAULT_USER}@${VAULT_HOST}" "cat '$remote_path'" 2>/dev/null)
    
    if [[ -z "$key_data" ]]; then
        echo "ERROR: Failed to fetch ${key_type} key from Vault" >&2
        return 1
    fi
    
    # Cache in /dev/shm (memory-only, survives for session)
    echo "$key_data" > "$cache_file"
    chmod 600 "$cache_file"
    
    echo "$key_data"
}

# ── Verify HMAC signature ──
verify_hmac() {
    local enc_file="$1"
    local sig_file="${enc_file%.enc}.sig"

    [[ ! -f "$sig_file" ]] && return 0  # No sig file = skip (backward compat)

    local hmac_key; hmac_key=$(fetch_key "hmac") || return 1

    local expected actual
    expected=$(cat "$sig_file")
    actual=$(openssl dgst -sha256 -hmac "$hmac_key" "$enc_file" 2>/dev/null | awk '{print $NF}')

    # Timing-safe comparison (prevents timing attacks)
    if command -v python3 &>/dev/null; then
        python3 -c "import hmac, sys; sys.exit(0 if hmac.compare_digest('$expected', '$actual') else 1)" 2>/dev/null
        return $?
    fi
    # Fallback: constant-time compare via openssl
    [[ "$expected" == "$actual" ]]
}

# ── Decrypt file ──
decrypt_file() {
    local enc_file="$1" output_file="$2"
    local aes_key; aes_key=$(fetch_key "aes") || return 1

    umask 077
    local key_file; key_file=$(mktemp)
    printf '%s' "$aes_key" > "$key_file"

    openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 \
        -pass "file:${key_file}" \
        -in "$enc_file" \
        -out "$output_file" 2>/dev/null
    local rc=$?

    # Secure wipe key file
    dd if=/dev/urandom of="$key_file" bs=$(stat -c %s "$key_file" 2>/dev/null || echo 4096) count=1 2>/dev/null
    rm -f "$key_file"

    if [[ $rc -ne 0 ]]; then
        echo "ERROR: Decryption failed for $(basename "$enc_file")" >&2
        rm -f "$output_file"
        return 1
    fi
    chmod 700 "$output_file"
    return 0
}

# ── Shred plaintext ──
shred_file() {
    local file="$1"
    if command -v shred &>/dev/null; then
        shred -u -z -n 3 "$file" 2>/dev/null
    else
        dd if=/dev/urandom of="$file" bs=$(stat -c %s "$file" 2>/dev/null || echo 4096) count=1 2>/dev/null
        rm -f "$file"
    fi
}

# ── Main ──
main() {
    local enc_file="$1"
    shift
    local args=("$@")
    
    # Resolve enc file path
    if [[ ! -f "$enc_file" ]]; then
        # Try in TOOL_DIR
        if [[ -f "${TOOL_DIR}/${enc_file}" ]]; then
            enc_file="${TOOL_DIR}/${enc_file}"
        elif [[ -f "${TOOL_DIR}/${enc_file}.enc" ]]; then
            enc_file="${TOOL_DIR}/${enc_file}.enc"
        else
            echo "ERROR: Tool file not found: $enc_file" >&2
            exit 1
        fi
    fi
    
    local basename
    basename=$(basename "$enc_file" .enc)
    local plain_file="${SHM_DIR}/${basename}"
    
    # 1. Verify HMAC
    if ! verify_hmac "$enc_file"; then
        exit 2
    fi
    
    # 2. Decrypt to /dev/shm
    if ! decrypt_file "$enc_file" "$plain_file"; then
        exit 3
    fi
    
    # 3. Execute
    local exit_code=0
    bash "$plain_file" "${args[@]}" || exit_code=$?
    
    # 4. Shred plaintext (always, even on error)
    shred_file "$plain_file"
    
    return $exit_code
}

# ── Entrypoint ──
if [[ $# -lt 1 ]]; then
    echo "Usage: $0 <tool_file.enc> [args...]"
    echo "Env: NODE_ID=$NODE_ID TOOL_DIR=$TOOL_DIR VAULT_HOST=$VAULT_HOST"
    exit 1
fi

main "$@"
