Version 1.1.3 terminalphone.sh
This commit is contained in:
parent
55fff2742e
commit
cd64ac0d6b
1 changed files with 335 additions and 32 deletions
367
terminalphone.sh
367
terminalphone.sh
|
|
@ -9,7 +9,7 @@ set -euo pipefail
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
APP_NAME="TerminalPhone"
|
APP_NAME="TerminalPhone"
|
||||||
VERSION="1.1.2"
|
VERSION="1.1.3"
|
||||||
BASE_DIR="$(dirname "$(readlink -f "$0")")"
|
BASE_DIR="$(dirname "$(readlink -f "$0")")"
|
||||||
DATA_DIR="$BASE_DIR/.terminalphone"
|
DATA_DIR="$BASE_DIR/.terminalphone"
|
||||||
TOR_DIR="$DATA_DIR/tor_data"
|
TOR_DIR="$DATA_DIR/tor_data"
|
||||||
|
|
@ -24,6 +24,8 @@ CONNECTED_FLAG="$DATA_DIR/run/connected_$$"
|
||||||
RECV_PIPE="$DATA_DIR/run/recv_$$"
|
RECV_PIPE="$DATA_DIR/run/recv_$$"
|
||||||
SEND_PIPE="$DATA_DIR/run/send_$$"
|
SEND_PIPE="$DATA_DIR/run/send_$$"
|
||||||
CIPHER_RUNTIME_FILE="$DATA_DIR/run/cipher_$$"
|
CIPHER_RUNTIME_FILE="$DATA_DIR/run/cipher_$$"
|
||||||
|
HMAC_RUNTIME_FILE="$DATA_DIR/run/hmac_$$"
|
||||||
|
NONCE_LOG_FILE="$DATA_DIR/run/nonces_$$"
|
||||||
AUTO_LISTEN_FLAG="$DATA_DIR/run/autolisten_$$"
|
AUTO_LISTEN_FLAG="$DATA_DIR/run/autolisten_$$"
|
||||||
AUTO_LISTEN_PID=""
|
AUTO_LISTEN_PID=""
|
||||||
|
|
||||||
|
|
@ -43,6 +45,8 @@ VOICE_EFFECT="none" # Voice effect (none, deep, high, robot, echo, whisper, cu
|
||||||
VOL_PTT=0 # Volume-down double-tap PTT (Termux only, experimental)
|
VOL_PTT=0 # Volume-down double-tap PTT (Termux only, experimental)
|
||||||
SHOW_CIRCUIT=0 # Show Tor circuit hops in call header (off by default)
|
SHOW_CIRCUIT=0 # Show Tor circuit hops in call header (off by default)
|
||||||
TOR_CONTROL_PORT=9051 # Tor control port (used when SHOW_CIRCUIT=1)
|
TOR_CONTROL_PORT=9051 # Tor control port (used when SHOW_CIRCUIT=1)
|
||||||
|
EXCLUDE_NODES="" # Tor ExcludeNodes (comma-separated country codes, e.g. {US},{GB})
|
||||||
|
HMAC_AUTH=0 # HMAC-sign all protocol messages (off by default)
|
||||||
|
|
||||||
# Custom voice effect parameters (used when VOICE_EFFECT=custom)
|
# Custom voice effect parameters (used when VOICE_EFFECT=custom)
|
||||||
VOICE_PITCH=0 # Pitch shift in cents (-600 to +600, 0=off)
|
VOICE_PITCH=0 # Pitch shift in cents (-600 to +600, 0=off)
|
||||||
|
|
@ -236,6 +240,8 @@ VOICE_HIGHPASS=$VOICE_HIGHPASS
|
||||||
VOICE_TREMOLO=$VOICE_TREMOLO
|
VOICE_TREMOLO=$VOICE_TREMOLO
|
||||||
VOL_PTT=$VOL_PTT
|
VOL_PTT=$VOL_PTT
|
||||||
SHOW_CIRCUIT=$SHOW_CIRCUIT
|
SHOW_CIRCUIT=$SHOW_CIRCUIT
|
||||||
|
EXCLUDE_NODES="$EXCLUDE_NODES"
|
||||||
|
HMAC_AUTH=$HMAC_AUTH
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,6 +385,16 @@ EOF
|
||||||
log_info "ControlPort enabled for circuit display"
|
log_info "ControlPort enabled for circuit display"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Append ExcludeNodes if configured
|
||||||
|
if [ -n "$EXCLUDE_NODES" ]; then
|
||||||
|
cat >> "$TOR_CONF" << EOF
|
||||||
|
|
||||||
|
ExcludeNodes $EXCLUDE_NODES
|
||||||
|
StrictNodes 1
|
||||||
|
EOF
|
||||||
|
log_info "ExcludeNodes: $EXCLUDE_NODES"
|
||||||
|
fi
|
||||||
|
|
||||||
# Append snowflake bridge config if enabled
|
# Append snowflake bridge config if enabled
|
||||||
if [ "$SNOWFLAKE_ENABLED" -eq 1 ] && check_dep snowflake-client; then
|
if [ "$SNOWFLAKE_ENABLED" -eq 1 ] && check_dep snowflake-client; then
|
||||||
local sf_bin
|
local sf_bin
|
||||||
|
|
@ -777,6 +793,60 @@ decrypt_file() {
|
||||||
-in "$infile" -out "$outfile" 3<<< "${SHARED_SECRET}" 2>/dev/null
|
-in "$infile" -out "$outfile" 3<<< "${SHARED_SECRET}" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# PROTOCOL SEND / VERIFY (HMAC)
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
# Send a protocol message, optionally HMAC-signed with random nonce
|
||||||
|
proto_send() {
|
||||||
|
local msg="$1"
|
||||||
|
local _hmac=0
|
||||||
|
[ -f "$HMAC_RUNTIME_FILE" ] && _hmac=$(cat "$HMAC_RUNTIME_FILE" 2>/dev/null)
|
||||||
|
[ -z "$_hmac" ] && _hmac="$HMAC_AUTH"
|
||||||
|
if [ "$_hmac" -eq 1 ]; then
|
||||||
|
local nonce sig signed_msg
|
||||||
|
nonce=$(head -c 8 /dev/urandom | od -An -tx1 | tr -d ' \n')
|
||||||
|
signed_msg="${nonce}:${msg}"
|
||||||
|
sig=$(printf '%s' "$signed_msg" | openssl dgst -sha256 -hmac "$SHARED_SECRET" -r 2>/dev/null | cut -d' ' -f1)
|
||||||
|
echo "${signed_msg}|${sig}" >&4 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "$msg" >&4 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify HMAC on a received message
|
||||||
|
# Outputs the raw message (without nonce) on stdout; returns 1 on failure
|
||||||
|
proto_verify() {
|
||||||
|
local line="$1"
|
||||||
|
local _hmac=0
|
||||||
|
[ -f "$HMAC_RUNTIME_FILE" ] && _hmac=$(cat "$HMAC_RUNTIME_FILE" 2>/dev/null)
|
||||||
|
[ -z "$_hmac" ] && _hmac="$HMAC_AUTH"
|
||||||
|
if [ "$_hmac" -ne 1 ]; then
|
||||||
|
echo "$line"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# Must contain | separator for HMAC
|
||||||
|
if [[ "$line" != *"|"* ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local signed_msg="${line%|*}"
|
||||||
|
local received_sig="${line##*|}"
|
||||||
|
local expected_sig
|
||||||
|
expected_sig=$(printf '%s' "$signed_msg" | openssl dgst -sha256 -hmac "$SHARED_SECRET" -r 2>/dev/null | cut -d' ' -f1)
|
||||||
|
if [ "$received_sig" = "$expected_sig" ]; then
|
||||||
|
# Reject replayed nonces
|
||||||
|
local nonce="${signed_msg%%:*}"
|
||||||
|
if grep -qF "$nonce" "$NONCE_LOG_FILE" 2>/dev/null; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$nonce" >> "$NONCE_LOG_FILE" 2>/dev/null
|
||||||
|
# Strip nonce prefix (nonce:message → message)
|
||||||
|
echo "${signed_msg#*:}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
# AUDIO PIPELINE
|
# AUDIO PIPELINE
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
|
|
@ -920,7 +990,7 @@ stop_and_send() {
|
||||||
|
|
||||||
local b64
|
local b64
|
||||||
b64=$(base64 -w 0 "$enc_file" 2>/dev/null)
|
b64=$(base64 -w 0 "$enc_file" 2>/dev/null)
|
||||||
echo "AUDIO:${b64}" >&4 2>/dev/null || true
|
proto_send "AUDIO:${b64}"
|
||||||
LAST_SENT_INFO="${size_whole}.${size_frac}KB"
|
LAST_SENT_INFO="${size_whole}.${size_frac}KB"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
@ -1012,6 +1082,8 @@ cleanup_call() {
|
||||||
rm -f "$PTT_FLAG" "$CONNECTED_FLAG"
|
rm -f "$PTT_FLAG" "$CONNECTED_FLAG"
|
||||||
rm -f "$RECV_PIPE" "$SEND_PIPE"
|
rm -f "$RECV_PIPE" "$SEND_PIPE"
|
||||||
rm -f "$CIPHER_RUNTIME_FILE"
|
rm -f "$CIPHER_RUNTIME_FILE"
|
||||||
|
rm -f "$HMAC_RUNTIME_FILE"
|
||||||
|
rm -f "$NONCE_LOG_FILE"
|
||||||
rm -f "$DATA_DIR/run/remote_id_$$"
|
rm -f "$DATA_DIR/run/remote_id_$$"
|
||||||
rm -f "$DATA_DIR/run/remote_cipher_$$"
|
rm -f "$DATA_DIR/run/remote_cipher_$$"
|
||||||
rm -f "$DATA_DIR/run/incoming_$$"
|
rm -f "$DATA_DIR/run/incoming_$$"
|
||||||
|
|
@ -1492,6 +1564,8 @@ in_call_session() {
|
||||||
|
|
||||||
# Write cipher to runtime file so subshells can track changes
|
# Write cipher to runtime file so subshells can track changes
|
||||||
echo "$CIPHER" > "$CIPHER_RUNTIME_FILE"
|
echo "$CIPHER" > "$CIPHER_RUNTIME_FILE"
|
||||||
|
echo "$HMAC_AUTH" > "$HMAC_RUNTIME_FILE"
|
||||||
|
: > "$NONCE_LOG_FILE"
|
||||||
|
|
||||||
# Open persistent file descriptors for the pipes
|
# Open persistent file descriptors for the pipes
|
||||||
exec 3< "$recv_pipe" # fd 3 = read from remote
|
exec 3< "$recv_pipe" # fd 3 = read from remote
|
||||||
|
|
@ -1501,9 +1575,9 @@ in_call_session() {
|
||||||
local my_onion
|
local my_onion
|
||||||
my_onion=$(get_onion)
|
my_onion=$(get_onion)
|
||||||
if [ -n "$my_onion" ]; then
|
if [ -n "$my_onion" ]; then
|
||||||
echo "ID:${my_onion}" >&4 2>/dev/null || true
|
proto_send "ID:${my_onion}"
|
||||||
fi
|
fi
|
||||||
echo "CIPHER:${CIPHER}" >&4 2>/dev/null || true
|
proto_send "CIPHER:${CIPHER}"
|
||||||
|
|
||||||
# Remote address and cipher (populated by handshake / receive loop)
|
# Remote address and cipher (populated by handshake / receive loop)
|
||||||
local remote_id_file="$DATA_DIR/run/remote_id_$$"
|
local remote_id_file="$DATA_DIR/run/remote_id_$$"
|
||||||
|
|
@ -1517,6 +1591,7 @@ in_call_session() {
|
||||||
# Read first line — should be ID
|
# Read first line — should be ID
|
||||||
local first_line=""
|
local first_line=""
|
||||||
if read -r -t 3 first_line <&3 2>/dev/null; then
|
if read -r -t 3 first_line <&3 2>/dev/null; then
|
||||||
|
first_line=$(proto_verify "$first_line") || first_line=""
|
||||||
if [[ "$first_line" == ID:* ]]; then
|
if [[ "$first_line" == ID:* ]]; then
|
||||||
remote_display="${first_line#ID:}"
|
remote_display="${first_line#ID:}"
|
||||||
echo "$remote_display" > "$remote_id_file"
|
echo "$remote_display" > "$remote_id_file"
|
||||||
|
|
@ -1530,6 +1605,7 @@ in_call_session() {
|
||||||
if [ -z "$remote_cipher" ]; then
|
if [ -z "$remote_cipher" ]; then
|
||||||
local cline=""
|
local cline=""
|
||||||
if read -r -t 1 cline <&3 2>/dev/null; then
|
if read -r -t 1 cline <&3 2>/dev/null; then
|
||||||
|
cline=$(proto_verify "$cline") || cline=""
|
||||||
if [[ "$cline" == CIPHER:* ]]; then
|
if [[ "$cline" == CIPHER:* ]]; then
|
||||||
remote_cipher="${cline#CIPHER:}"
|
remote_cipher="${cline#CIPHER:}"
|
||||||
fi
|
fi
|
||||||
|
|
@ -1559,6 +1635,7 @@ in_call_session() {
|
||||||
while [ -f "$CONNECTED_FLAG" ]; do
|
while [ -f "$CONNECTED_FLAG" ]; do
|
||||||
local line=""
|
local line=""
|
||||||
if read -r line <&3 2>/dev/null; then
|
if read -r line <&3 2>/dev/null; then
|
||||||
|
line=$(proto_verify "$line") || continue
|
||||||
case "$line" in
|
case "$line" in
|
||||||
PTT_START)
|
PTT_START)
|
||||||
printf '\033[s' >&2
|
printf '\033[s' >&2
|
||||||
|
|
@ -1695,7 +1772,7 @@ in_call_session() {
|
||||||
else
|
else
|
||||||
ptt_active=0
|
ptt_active=0
|
||||||
stop_and_send
|
stop_and_send
|
||||||
echo "PTT_STOP" >&4 2>/dev/null || true
|
proto_send "PTT_STOP"
|
||||||
# Update Last sent + status bar
|
# Update Last sent + status bar
|
||||||
printf '\033[s' >&2
|
printf '\033[s' >&2
|
||||||
printf '\033[%d;1H\033[K' "$SENT_INFO_ROW" >&2
|
printf '\033[%d;1H\033[K' "$SENT_INFO_ROW" >&2
|
||||||
|
|
@ -1729,7 +1806,7 @@ in_call_session() {
|
||||||
REC_FILE=""
|
REC_FILE=""
|
||||||
fi
|
fi
|
||||||
echo -e "\r\n${YELLOW}Hanging up...${NC}" >&2
|
echo -e "\r\n${YELLOW}Hanging up...${NC}" >&2
|
||||||
echo "HANGUP" >&4 2>/dev/null || true
|
proto_send "HANGUP"
|
||||||
rm -f "$PTT_FLAG" "$CONNECTED_FLAG"
|
rm -f "$PTT_FLAG" "$CONNECTED_FLAG"
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -1739,7 +1816,7 @@ in_call_session() {
|
||||||
stty time 1 # restore fast timeout for key detection
|
stty time 1 # restore fast timeout for key detection
|
||||||
ptt_active=0
|
ptt_active=0
|
||||||
stop_and_send
|
stop_and_send
|
||||||
echo "PTT_STOP" >&4 2>/dev/null || true
|
proto_send "PTT_STOP"
|
||||||
# Update Last sent + status bar
|
# Update Last sent + status bar
|
||||||
printf '\033[s' >&2
|
printf '\033[s' >&2
|
||||||
printf '\033[%d;1H\033[K' "$SENT_INFO_ROW" >&2
|
printf '\033[%d;1H\033[K' "$SENT_INFO_ROW" >&2
|
||||||
|
|
@ -1767,7 +1844,7 @@ in_call_session() {
|
||||||
if [ -s "$chat_enc" ]; then
|
if [ -s "$chat_enc" ]; then
|
||||||
local chat_b64
|
local chat_b64
|
||||||
chat_b64=$(base64 -w 0 "$chat_enc" 2>/dev/null)
|
chat_b64=$(base64 -w 0 "$chat_enc" 2>/dev/null)
|
||||||
echo "MSG:${chat_b64}" >&4 2>/dev/null || true
|
proto_send "MSG:${chat_b64}"
|
||||||
echo -e " ${DIM}[you] ${chat_msg}${NC}" >&2
|
echo -e " ${DIM}[you] ${chat_msg}${NC}" >&2
|
||||||
fi
|
fi
|
||||||
rm -f "$chat_plain" "$chat_enc" 2>/dev/null
|
rm -f "$chat_plain" "$chat_enc" 2>/dev/null
|
||||||
|
|
@ -1967,16 +2044,9 @@ settings_menu() {
|
||||||
while true; do
|
while true; do
|
||||||
clear
|
clear
|
||||||
echo -e "\n${BOLD}${CYAN}═══ Settings ═══${NC}\n"
|
echo -e "\n${BOLD}${CYAN}═══ Settings ═══${NC}\n"
|
||||||
echo -e " ${DIM}Current cipher: ${NC}${WHITE}${CIPHER}${NC}"
|
|
||||||
echo -e " ${DIM}Current Opus bitrate: ${NC}${WHITE}${OPUS_BITRATE} kbps${NC}"
|
echo -e " ${DIM}Current Opus bitrate: ${NC}${WHITE}${OPUS_BITRATE} kbps${NC}"
|
||||||
echo -e " ${DIM}Current Opus frame: ${NC}${WHITE}${OPUS_FRAMESIZE} ms${NC}"
|
echo -e " ${DIM}Current Opus frame: ${NC}${WHITE}${OPUS_FRAMESIZE} ms${NC}"
|
||||||
|
|
||||||
local sf_label="${RED}disabled${NC}"
|
|
||||||
if [ "$SNOWFLAKE_ENABLED" -eq 1 ]; then
|
|
||||||
sf_label="${GREEN}enabled${NC}"
|
|
||||||
fi
|
|
||||||
echo -e " ${DIM}Snowflake bridge: ${NC}${sf_label}"
|
|
||||||
|
|
||||||
local al_label="${RED}disabled${NC}"
|
local al_label="${RED}disabled${NC}"
|
||||||
if [ "$AUTO_LISTEN" -eq 1 ]; then
|
if [ "$AUTO_LISTEN" -eq 1 ]; then
|
||||||
al_label="${GREEN}enabled${NC}"
|
al_label="${GREEN}enabled${NC}"
|
||||||
|
|
@ -2004,28 +2074,31 @@ settings_menu() {
|
||||||
circ_label="${GREEN}enabled${NC}"
|
circ_label="${GREEN}enabled${NC}"
|
||||||
fi
|
fi
|
||||||
echo -e " ${DIM}Circuit display: ${NC}${circ_label}"
|
echo -e " ${DIM}Circuit display: ${NC}${circ_label}"
|
||||||
|
|
||||||
|
local hmac_label="${RED}disabled${NC}"
|
||||||
|
if [ "$HMAC_AUTH" -eq 1 ]; then
|
||||||
|
hmac_label="${GREEN}enabled${NC}"
|
||||||
|
fi
|
||||||
|
echo -e " ${DIM}HMAC auth: ${NC}${hmac_label}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Change encryption cipher"
|
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Change Opus encoding quality"
|
||||||
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} Change Opus encoding quality"
|
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} Auto-listen (listen for calls automatically once Tor starts)"
|
||||||
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}│${NC} Snowflake bridge (censorship circumvention)"
|
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}│${NC} Change PTT (push-to-talk) key"
|
||||||
echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}│${NC} Auto-listen (listen for calls automatically once Tor starts)"
|
echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}│${NC} Voice changer"
|
||||||
echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}│${NC} Change PTT (push-to-talk) key"
|
|
||||||
echo -e " ${BOLD}${WHITE}6${NC} ${CYAN}│${NC} Voice changer"
|
|
||||||
if [ $IS_TERMUX -eq 1 ]; then
|
if [ $IS_TERMUX -eq 1 ]; then
|
||||||
echo -e " ${BOLD}${WHITE}7${NC} ${CYAN}│${NC} Volume PTT ${DIM}(double-tap Vol Down to talk, experimental)${NC}"
|
echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}│${NC} Volume PTT ${DIM}(double-tap Vol Down to talk, experimental)${NC}"
|
||||||
fi
|
fi
|
||||||
echo -e " ${BOLD}${WHITE}8${NC} ${CYAN}│${NC} Tor settings"
|
echo -e " ${BOLD}${WHITE}6${NC} ${CYAN}│${NC} Tor settings"
|
||||||
|
echo -e " ${BOLD}${WHITE}7${NC} ${CYAN}│${NC} Security"
|
||||||
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back to main menu${NC}"
|
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back to main menu${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -ne " ${BOLD}Select: ${NC}"
|
echo -ne " ${BOLD}Select: ${NC}"
|
||||||
read -r schoice
|
read -r schoice
|
||||||
|
|
||||||
case "$schoice" in
|
case "$schoice" in
|
||||||
1) settings_cipher ;;
|
1) settings_opus ;;
|
||||||
2) settings_opus ;;
|
2)
|
||||||
3) settings_snowflake ;;
|
|
||||||
4)
|
|
||||||
if [ "$AUTO_LISTEN" -eq 1 ]; then
|
if [ "$AUTO_LISTEN" -eq 1 ]; then
|
||||||
AUTO_LISTEN=0
|
AUTO_LISTEN=0
|
||||||
stop_auto_listener
|
stop_auto_listener
|
||||||
|
|
@ -2038,7 +2111,7 @@ settings_menu() {
|
||||||
save_config
|
save_config
|
||||||
sleep 1
|
sleep 1
|
||||||
;;
|
;;
|
||||||
5)
|
3)
|
||||||
local _pd="SPACE"
|
local _pd="SPACE"
|
||||||
[ "$PTT_KEY" != " " ] && _pd="$PTT_KEY"
|
[ "$PTT_KEY" != " " ] && _pd="$PTT_KEY"
|
||||||
echo -e "\n ${DIM}Current PTT key: ${NC}${WHITE}${_pd}${NC}"
|
echo -e "\n ${DIM}Current PTT key: ${NC}${WHITE}${_pd}${NC}"
|
||||||
|
|
@ -2059,8 +2132,8 @@ settings_menu() {
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
;;
|
;;
|
||||||
6) settings_voice ;;
|
4) settings_voice ;;
|
||||||
7)
|
5)
|
||||||
if [ $IS_TERMUX -eq 1 ]; then
|
if [ $IS_TERMUX -eq 1 ]; then
|
||||||
if [ "$VOL_PTT" -eq 1 ]; then
|
if [ "$VOL_PTT" -eq 1 ]; then
|
||||||
VOL_PTT=0
|
VOL_PTT=0
|
||||||
|
|
@ -2095,7 +2168,8 @@ settings_menu() {
|
||||||
sleep 1
|
sleep 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
8) settings_tor ;;
|
6) settings_tor ;;
|
||||||
|
7) settings_security ;;
|
||||||
0|q|Q) return ;;
|
0|q|Q) return ;;
|
||||||
*)
|
*)
|
||||||
echo -e "\n ${RED}Invalid choice${NC}"
|
echo -e "\n ${RED}Invalid choice${NC}"
|
||||||
|
|
@ -2190,7 +2264,7 @@ settings_cipher() {
|
||||||
[ -f "$CIPHER_RUNTIME_FILE" ] && echo "$CIPHER" > "$CIPHER_RUNTIME_FILE"
|
[ -f "$CIPHER_RUNTIME_FILE" ] && echo "$CIPHER" > "$CIPHER_RUNTIME_FILE"
|
||||||
# Notify remote side if in a call
|
# Notify remote side if in a call
|
||||||
if [ "$CALL_ACTIVE" -eq 1 ]; then
|
if [ "$CALL_ACTIVE" -eq 1 ]; then
|
||||||
echo "CIPHER:${CIPHER}" >&4 2>/dev/null || true
|
proto_send "CIPHER:${CIPHER}"
|
||||||
fi
|
fi
|
||||||
echo -e "\n ${GREEN}${BOLD}✓${NC} Cipher set to ${WHITE}${BOLD}${CIPHER}${NC}"
|
echo -e "\n ${GREEN}${BOLD}✓${NC} Cipher set to ${WHITE}${BOLD}${CIPHER}${NC}"
|
||||||
else
|
else
|
||||||
|
|
@ -2480,10 +2554,22 @@ settings_tor() {
|
||||||
if [ "$SHOW_CIRCUIT" -eq 1 ]; then
|
if [ "$SHOW_CIRCUIT" -eq 1 ]; then
|
||||||
circ_label="${GREEN}enabled${NC}"
|
circ_label="${GREEN}enabled${NC}"
|
||||||
fi
|
fi
|
||||||
|
local excl_label="${DIM}none${NC}"
|
||||||
|
if [ -n "$EXCLUDE_NODES" ]; then
|
||||||
|
excl_label="${YELLOW}${EXCLUDE_NODES}${NC}"
|
||||||
|
fi
|
||||||
|
local sf_label="${RED}disabled${NC}"
|
||||||
|
if [ "$SNOWFLAKE_ENABLED" -eq 1 ]; then
|
||||||
|
sf_label="${GREEN}enabled${NC}"
|
||||||
|
fi
|
||||||
echo -e " ${DIM}Circuit display: ${NC}${circ_label}"
|
echo -e " ${DIM}Circuit display: ${NC}${circ_label}"
|
||||||
|
echo -e " ${DIM}Exclude nodes: ${NC}${excl_label}"
|
||||||
|
echo -e " ${DIM}Snowflake bridge: ${NC}${sf_label}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Toggle circuit hop display in calls"
|
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Toggle circuit hop display in calls"
|
||||||
|
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} Exclude countries from circuits"
|
||||||
|
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}│${NC} Snowflake bridge (censorship circumvention)"
|
||||||
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back${NC}"
|
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -ne " ${BOLD}Select: ${NC}"
|
echo -ne " ${BOLD}Select: ${NC}"
|
||||||
|
|
@ -2503,6 +2589,223 @@ settings_tor() {
|
||||||
echo -e " ${DIM}Restart Tor for changes to take effect (Main menu → option 10).${NC}"
|
echo -e " ${DIM}Restart Tor for changes to take effect (Main menu → option 10).${NC}"
|
||||||
sleep 2
|
sleep 2
|
||||||
;;
|
;;
|
||||||
|
2) settings_exclude_nodes ;;
|
||||||
|
3) settings_snowflake ;;
|
||||||
|
0|q|Q) return ;;
|
||||||
|
*)
|
||||||
|
echo -e "\n ${RED}Invalid choice${NC}"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_exclude_nodes() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "\n${BOLD}${CYAN}═══ Exclude Countries ═══${NC}\n"
|
||||||
|
|
||||||
|
if [ -n "$EXCLUDE_NODES" ]; then
|
||||||
|
echo -e " ${DIM}Current:${NC} ${YELLOW}${EXCLUDE_NODES}${NC}"
|
||||||
|
else
|
||||||
|
echo -e " ${DIM}Current:${NC} ${DIM}none (all countries allowed)${NC}"
|
||||||
|
fi
|
||||||
|
echo -e " ${DIM}Tor will avoid building circuits through excluded countries.${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Five Eyes ${DIM}(US, GB, CA, AU, NZ)${NC}"
|
||||||
|
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} Nine Eyes ${DIM}(+ DK, FR, NL, NO)${NC}"
|
||||||
|
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}│${NC} Fourteen Eyes ${DIM}(+ DE, BE, IT, SE, ES)${NC}"
|
||||||
|
echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}│${NC} Custom countries"
|
||||||
|
echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}│${NC} ${RED}Clear (allow all)${NC}"
|
||||||
|
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -ne " ${BOLD}Select: ${NC}"
|
||||||
|
read -r _excl_choice
|
||||||
|
|
||||||
|
case "$_excl_choice" in
|
||||||
|
1)
|
||||||
|
EXCLUDE_NODES="{US},{GB},{CA},{AU},{NZ}"
|
||||||
|
save_config
|
||||||
|
log_ok "Excluding Five Eyes countries"
|
||||||
|
echo -e " ${DIM}Restart Tor for changes to take effect.${NC}"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
EXCLUDE_NODES="{US},{GB},{CA},{AU},{NZ},{DK},{FR},{NL},{NO}"
|
||||||
|
save_config
|
||||||
|
log_ok "Excluding Nine Eyes countries"
|
||||||
|
echo -e " ${DIM}Restart Tor for changes to take effect.${NC}"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
EXCLUDE_NODES="{US},{GB},{CA},{AU},{NZ},{DK},{FR},{NL},{NO},{DE},{BE},{IT},{SE},{ES}"
|
||||||
|
save_config
|
||||||
|
log_ok "Excluding Fourteen Eyes countries"
|
||||||
|
echo -e " ${DIM}Restart Tor for changes to take effect.${NC}"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
clear
|
||||||
|
echo -e "\n${BOLD}${CYAN}═══ Custom Country Exclusion ═══${NC}\n"
|
||||||
|
echo -e " ${DIM}Country code reference:${NC}\n"
|
||||||
|
echo -e " ${WHITE}AF${NC} Afghanistan ${WHITE}AL${NC} Albania ${WHITE}DZ${NC} Algeria"
|
||||||
|
echo -e " ${WHITE}AR${NC} Argentina ${WHITE}AM${NC} Armenia ${WHITE}AU${NC} Australia"
|
||||||
|
echo -e " ${WHITE}AT${NC} Austria ${WHITE}AZ${NC} Azerbaijan ${WHITE}BH${NC} Bahrain"
|
||||||
|
echo -e " ${WHITE}BD${NC} Bangladesh ${WHITE}BY${NC} Belarus ${WHITE}BE${NC} Belgium"
|
||||||
|
echo -e " ${WHITE}BR${NC} Brazil ${WHITE}BG${NC} Bulgaria ${WHITE}KH${NC} Cambodia"
|
||||||
|
echo -e " ${WHITE}CA${NC} Canada ${WHITE}CL${NC} Chile ${WHITE}CN${NC} China"
|
||||||
|
echo -e " ${WHITE}CO${NC} Colombia ${WHITE}HR${NC} Croatia ${WHITE}CU${NC} Cuba"
|
||||||
|
echo -e " ${WHITE}CY${NC} Cyprus ${WHITE}CZ${NC} Czech Republic ${WHITE}DK${NC} Denmark"
|
||||||
|
echo -e " ${WHITE}EG${NC} Egypt ${WHITE}EE${NC} Estonia ${WHITE}ET${NC} Ethiopia"
|
||||||
|
echo -e " ${WHITE}FI${NC} Finland ${WHITE}FR${NC} France ${WHITE}GE${NC} Georgia"
|
||||||
|
echo -e " ${WHITE}DE${NC} Germany ${WHITE}GR${NC} Greece ${WHITE}HK${NC} Hong Kong"
|
||||||
|
echo -e " ${WHITE}HU${NC} Hungary ${WHITE}IS${NC} Iceland ${WHITE}IN${NC} India"
|
||||||
|
echo -e " ${WHITE}ID${NC} Indonesia ${WHITE}IR${NC} Iran ${WHITE}IQ${NC} Iraq"
|
||||||
|
echo -e " ${WHITE}IE${NC} Ireland ${WHITE}IL${NC} Israel ${WHITE}IT${NC} Italy"
|
||||||
|
echo -e " ${WHITE}JP${NC} Japan ${WHITE}JO${NC} Jordan ${WHITE}KZ${NC} Kazakhstan"
|
||||||
|
echo -e " ${WHITE}KE${NC} Kenya ${WHITE}KP${NC} North Korea ${WHITE}KR${NC} South Korea"
|
||||||
|
echo -e " ${WHITE}KW${NC} Kuwait ${WHITE}LV${NC} Latvia ${WHITE}LB${NC} Lebanon"
|
||||||
|
echo -e " ${WHITE}LT${NC} Lithuania ${WHITE}LU${NC} Luxembourg ${WHITE}MY${NC} Malaysia"
|
||||||
|
echo -e " ${WHITE}MX${NC} Mexico ${WHITE}MD${NC} Moldova ${WHITE}MA${NC} Morocco"
|
||||||
|
echo -e " ${WHITE}NL${NC} Netherlands ${WHITE}NZ${NC} New Zealand ${WHITE}NG${NC} Nigeria"
|
||||||
|
echo -e " ${WHITE}NO${NC} Norway ${WHITE}PK${NC} Pakistan ${WHITE}PA${NC} Panama"
|
||||||
|
echo -e " ${WHITE}PH${NC} Philippines ${WHITE}PL${NC} Poland ${WHITE}PT${NC} Portugal"
|
||||||
|
echo -e " ${WHITE}QA${NC} Qatar ${WHITE}RO${NC} Romania ${WHITE}RU${NC} Russia"
|
||||||
|
echo -e " ${WHITE}SA${NC} Saudi Arabia ${WHITE}RS${NC} Serbia ${WHITE}SG${NC} Singapore"
|
||||||
|
echo -e " ${WHITE}SK${NC} Slovakia ${WHITE}SI${NC} Slovenia ${WHITE}ZA${NC} South Africa"
|
||||||
|
echo -e " ${WHITE}ES${NC} Spain ${WHITE}SE${NC} Sweden ${WHITE}CH${NC} Switzerland"
|
||||||
|
echo -e " ${WHITE}TW${NC} Taiwan ${WHITE}TH${NC} Thailand ${WHITE}TR${NC} Turkey"
|
||||||
|
echo -e " ${WHITE}UA${NC} Ukraine ${WHITE}AE${NC} UAE ${WHITE}GB${NC} United Kingdom"
|
||||||
|
echo -e " ${WHITE}US${NC} United States ${WHITE}UZ${NC} Uzbekistan ${WHITE}VN${NC} Vietnam"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${DIM}Enter codes in Tor format, comma-separated.${NC}"
|
||||||
|
echo -e " ${DIM}Example: {US},{GB},{DE},{RU},{CN}${NC}\n"
|
||||||
|
echo -ne " ${BOLD}Countries: ${NC}"
|
||||||
|
read -r _custom_nodes
|
||||||
|
if [ -n "$_custom_nodes" ]; then
|
||||||
|
EXCLUDE_NODES="$_custom_nodes"
|
||||||
|
save_config
|
||||||
|
log_ok "Excluding: $EXCLUDE_NODES"
|
||||||
|
else
|
||||||
|
log_warn "No input — nothing changed"
|
||||||
|
fi
|
||||||
|
echo -e " ${DIM}Restart Tor for changes to take effect.${NC}"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
EXCLUDE_NODES=""
|
||||||
|
save_config
|
||||||
|
log_ok "All countries allowed"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
0|q|Q) return ;;
|
||||||
|
*)
|
||||||
|
echo -e "\n ${RED}Invalid choice${NC}"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_security() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "\n${BOLD}${CYAN}═══ Security ═══${NC}\n"
|
||||||
|
|
||||||
|
local hmac_label="${RED}disabled${NC}"
|
||||||
|
if [ "$HMAC_AUTH" -eq 1 ]; then
|
||||||
|
hmac_label="${GREEN}enabled${NC}"
|
||||||
|
fi
|
||||||
|
local cipher_upper="${CIPHER^^}"
|
||||||
|
echo -e " ${DIM}Cipher: ${NC}${WHITE}${cipher_upper}${NC}"
|
||||||
|
echo -e " ${DIM}HMAC auth: ${NC}${hmac_label}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Change encryption cipher"
|
||||||
|
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} HMAC authentication"
|
||||||
|
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -ne " ${BOLD}Select: ${NC}"
|
||||||
|
read -r _sec_choice
|
||||||
|
|
||||||
|
case "$_sec_choice" in
|
||||||
|
1) settings_cipher ;;
|
||||||
|
2) settings_hmac ;;
|
||||||
|
0|q|Q) return ;;
|
||||||
|
*)
|
||||||
|
echo -e "\n ${RED}Invalid choice${NC}"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_hmac() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "\n${BOLD}${CYAN}═══ HMAC Authentication ═══${NC}\n"
|
||||||
|
|
||||||
|
local hmac_label="${RED}disabled${NC}"
|
||||||
|
if [ "$HMAC_AUTH" -eq 1 ]; then
|
||||||
|
hmac_label="${GREEN}enabled${NC}"
|
||||||
|
fi
|
||||||
|
echo -e " ${DIM}Status:${NC} ${hmac_label}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e " ${DIM}When enabled, every message sent during a call (voice,${NC}"
|
||||||
|
echo -e " ${DIM}text, hangup, and all control signals) is signed with${NC}"
|
||||||
|
echo -e " ${DIM}HMAC-SHA256 derived from your shared secret.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${DIM}A random nonce is included with each message so that${NC}"
|
||||||
|
echo -e " ${DIM}identical commands produce a unique signature every time.${NC}"
|
||||||
|
echo -e " ${DIM}This prevents replay attacks — a captured message cannot${NC}"
|
||||||
|
echo -e " ${DIM}be re-sent to disrupt future calls.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${DIM}On the receiving end, any message with an invalid or${NC}"
|
||||||
|
echo -e " ${DIM}missing signature is silently dropped. An attacker who${NC}"
|
||||||
|
echo -e " ${DIM}compromises the Tor circuit but does not have the shared${NC}"
|
||||||
|
echo -e " ${DIM}secret cannot inject commands like HANGUP to disconnect${NC}"
|
||||||
|
echo -e " ${DIM}your call or forge audio and text messages.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}Both parties must have HMAC enabled for calls to work.${NC}"
|
||||||
|
echo -e " ${YELLOW}Not compatible with versions prior to 1.1.3.${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}│${NC} Turn on"
|
||||||
|
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}│${NC} Turn off"
|
||||||
|
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}│${NC} ${DIM}Back${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -ne " ${BOLD}Select: ${NC}"
|
||||||
|
read -r _hmac_choice
|
||||||
|
|
||||||
|
case "$_hmac_choice" in
|
||||||
|
1)
|
||||||
|
if [ "$HMAC_AUTH" -eq 1 ]; then
|
||||||
|
log_info "HMAC authentication is already enabled"
|
||||||
|
else
|
||||||
|
HMAC_AUTH=1
|
||||||
|
save_config
|
||||||
|
log_ok "HMAC authentication enabled"
|
||||||
|
if [ "$CALL_ACTIVE" -eq 1 ]; then
|
||||||
|
echo -e " ${YELLOW}Takes effect on the next call.${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [ "$HMAC_AUTH" -eq 0 ]; then
|
||||||
|
log_info "HMAC authentication is already disabled"
|
||||||
|
else
|
||||||
|
HMAC_AUTH=0
|
||||||
|
save_config
|
||||||
|
log_ok "HMAC authentication disabled"
|
||||||
|
if [ "$CALL_ACTIVE" -eq 1 ]; then
|
||||||
|
echo -e " ${YELLOW}Takes effect on the next call.${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
0|q|Q) return ;;
|
0|q|Q) return ;;
|
||||||
*)
|
*)
|
||||||
echo -e "\n ${RED}Invalid choice${NC}"
|
echo -e "\n ${RED}Invalid choice${NC}"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue