#!/bin/bash
# ----------------
# GLOBAL VARIABLES
# ----------------
#
# By default this script will DECRYPT the ciphertext declared below using the IV also declared below
# If you want to ENCRYPT instead, enter your cleartext in the "ciphertext" variable
# And change the value of DECRYPT to 0
# Remember to ALLWAYS enter UPPER TEXT.
#
# You may also want to change the suits "couple" order, right now it is set to Higher, Higher, Lower, Lower.
#
#
ORDER="HHLL"

# DECRYPT = 1 means we want to decrypt, DECRYPT = 0 means we want to encrypt instead
DECRYPT=1
#ciphertect to be entered in UPPER case
ciphertext="ANHXJRAAZEBYYOMNWPBKGZOGY"

#IV to be entered in UPPER case
IV="WEMUSTFOLLOWTHEWHITERABBIT"

clear
echo "Pocket RC4 - By Bugs (@__bugs) - Version 5"
echo "This Shell script was written to answer the Hackhu2015 Challenge question 1"
echo "It only works with BASH 4.2+"
bashversion=`bash --version | head -n 1 | awk '{print $4}' | awk 'BEGIN { FS="."}; {print $1$2}'`

if [ "$bashversion" -ge "42" ];
then
	echo "Bash version installed on this system is recent enough. We can continue"
else
	echo
	echo "[WARNING!!!] YOUR VERSION OF BASH APPEARS TO BE TOO LOW:"
	bash --version | head -n 1
	echo "Lower version of Bash will not run this script properly because we need the function 'declare -A' for it to work"
	echo
	echo "Press a key if you still want to continue or CTRL-C to exit."
	echo "If this script does not complete within a few seconds then you need to upgrade Bash to a newer version"
	read key -n 1
fi

echo
echo "You can edit this shell script to change the settings."
echo "-> Current settings are:"
if [ "$DECRYPT" -eq "1" ];
then
	echo "- DECRYPT"
else
	echo "- ENCRYPT"
fi
echo "- Suits Higher/Lower Order = $ORDER"
echo "- IV = $IV"
echo "- TEXT = $ciphertext"
echo
echo "-> Press any key to continue or CTRL-C to stop"
read key -n 1
	
j=1
cipher_length=${#ciphertext}
IV_length=${#IV}

declare -A letter_to_value

for i in {A..Z}; do
letter_to_value+=( [$i]=$j )
value_to_letter+=( [$j]=$i )
((j++))
done



for (( i=0; i<$IV_length; i++)); do
	IV_NUM[i+1]=${letter_to_value[${IV:$i:1}]}
done

echo "-> IV Converted"
echo "From $IV to ${IV_NUM[@]}"

if [ "$ORDER" == "HHLL" ]; then
	ia=14
	ib=14
	ic=1
	id=1
else
	if [ "$ORDER" == "HLHL" ]; then
		ia=14
		ib=1
		ic=14
		id=1
	else
		if [ "$ORDER" == "LLHH" ]; then
			ia=1
			ib=1
			ic=14
			id=14
		else
			if [ "$ORDER" == "LHLH" ]; then
				ia=1
				ib=14
				ic=1
				id=14
			else
				echo "[ERROR] ORDER Value must be one of the following 4: HHLL, HLHL, LHLH or LLHH."
				echo "-> STOPPING"
				exit
			fi
		fi
	fi
fi

echo "-> Creating Deck of cards Odd (Red) and Even (Black)"
for ((position = 1, i = $ia, j = $ib; position < 26; position = position + 2, i++, j++)); do
 deck[$position]=$i
 deck[$(($position+1))]=$j
# echo "position = $position"
done

echo "-> Creating Deck of cards Odd (Red) and Even (Black)"
for ((position = 27, i = $ic, j = $id; position < 52; position = position + 2, i++, j++)); do
 deck[$position]=$j
 deck[$(($position+1))]=$i
#  echo "position = $position"
done

	echo
	echo -n "Initial Deck Status:"				
	echo ${deck[@]}

#Initialise the deck with each letter of the IV
echo "-> Initialising Deck with IV"

for ((i = 0; i<$IV_length; i++)); do
	card=${IV_NUM[i+1]}
	j=1
	found=0

	while [ "$found" -eq "0" ]; do
		if [ "${deck[$j]}" != "$card" ];
		then
			((j++))
		else
			echo -n "==> Found value IV card '$card' at position $j"
			check_even=$(( $j % 2 ))

#Locate the black card in the key deck corresponding to the IV letter.	
			if [ $check_even -eq 0 ];
			then
				black_a=${deck[$j]}
				red_a=${deck[(( $j - 1 ))]}
#Exchange the red card immediately above this black card with the top card (also red).
				echo -n ". This is a Black card"
				echo
				echo -n "-> Exchanging Top Red Card '${deck[1]}' with '$red_a'"
				deck[(($j - 1))]=${deck[1]}
				deck[1]=$red_a
				red_a=${deck[(( $j - 1 ))]}
			
				echo ". Top Exchange Deck Status:"				
				echo -n ${deck[@]}

#Move the black IV card and the next red card immediately
# above this black card (red) to the bottom of the deck.				
				echo
				echo -n "-> Move the IV card and Red to bottom"
				for ((k = (($j - 1)); k < 51; k++)); do
					deck[$k]=${deck[(($k + 2))]}
				done
				
				deck[51]=$red_a
				deck[52]=$black_a

				echo ". IV Move Deck Status:"				
				echo -n ${deck[@]}
				
#Move the top two cards on the deck (one red and one black) to the bottom of the deck.
				echo
				echo -n "-> Moving Top Red and Black cards"				
				red_a=${deck[1]}
				black_a=${deck[2]}
				
				for ((k = 1; k < 51; k++)); do
 					deck[$k]=${deck[(($k + 2))]}
				done
				
				deck[51]=$red_a
				deck[52]=$black_a
				
				echo ". Top Move Deck Status:"				
 				echo -n " ${deck[@]}"
				echo 
				found=1
			else
				((j++))
				echo -n ". This is a Red card"		
			fi
		fi
		if [ "$j" -gt 52 ];
		then
			j=1
		fi
	done
done
 
echo "-> Deck Status after IV initialisation:"
echo ${deck[@]}
echo "-> Now starting the cipher"

	
#For each letter of the cipher
for ((index = 0; index<$cipher_length; index++)); do

# Set j to the value of the bottom red card. 
	j=${deck[51]}
	echo ". Bottom RED card value: j=$card"

# Add the value of the top red card to j modulo 27
	j=$(( ($j + ${deck[1]}) % 26 ))
	if [ "$j" -eq 0 ]; 
	then
		j=26
	fi

	echo ". Top red card '${deck[1]}' + j % 26 = $j. Findings its black card."

	found=0
	position=1

	while [ "$found" -eq "0" ]; do
		if [ "${deck[$position]}" != "$j" ];
		then
			((position++))
		else
			check_even=$(( $position % 2 ))
			
#Find the black card corresponding to 'j'.
			if [ $check_even -eq 0 ];
			then
				red_a=${deck[(( $position - 1 ))]}
				echo -n ". Found Black card"
				echo ". Adding above Red card '$red_a' to Top Red Card '${deck[1]}' % 26"
				k=$(( ($red_a + ${deck[1]}) % 26))
				if [ "$k" -eq 0 ]; 
				then
					k=26
				fi

				if [ "$DECRYPT" -eq "1" ];
				then						
					plaintext[$index]=$(( (${letter_to_value[${ciphertext:$index:1}]} - $k ) % 26 ))
				else
					plaintext[$index]=$(( (${letter_to_value[${ciphertext:$index:1}]} + $k ) % 26 ))

				fi

				if [ "${plaintext[$index]}" -eq 0 ]; 
				then
					plaintext[$index]=26
				fi
				
				if [ "${plaintext[$index]}" -lt 0 ]; 
				then
					plaintext[$index]=$(( ${plaintext[$index]} + 26 ))
				fi
	
				echo ". Ciphertext=${ciphertext:index:1} and Cipher text value '${letter_to_value[${ciphertext:index:1}]}' - k=$k = ${plaintext[$index]}"
				
				echo "-> Exchange the two red cards"
				deck[(( $position - 1 ))]=${deck[1]}
				deck[1]=$red_a
				
				echo ". Moving Top two cards to the bottom"
				
				red_a=${deck[1]}
				black_a=${deck[2]}
				
				for ((a = 1; a < 51; a++)); do
 					deck[$a]=${deck[(($a + 2))]}
				done
				
				deck[51]=$red_a
				deck[52]=$black_a
						
				echo ". Done"

				found=1
			else
				((position++))
				echo -n ". This is a Red card"		
			fi
		fi
		if [ "$position" -gt 52 ];
		then
			position=1
		fi
	done


done

echo "Et Voila!"
echo "Resulting Text is:"
echo ${plaintext[@]}
echo
echo "-> UPPERCASE Results:"
echo "IV = $IV"
echo -n "TEXT = "
for (( i=0; i<$cipher_length; i++)); do
        result[i]=${value_to_letter[${plaintext[$i]}]}
        echo -n "${value_to_letter[${plaintext[$i]}]}"
done
echo
echo
echo "-> Lowercase Results:"
echo "IV = ${IV,,}"
echo -n "TEXT = "
for (( i=0; i<$cipher_length; i++)); do
	result[i]=${value_to_letter[${plaintext[$i]}]}
	echo -n "${value_to_letter[${plaintext[$i]}],,}"
done
echo
