Skip to content

Instantly share code, notes, and snippets.

@bruceoutdoors
Last active June 2, 2022 21:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bruceoutdoors/0685335d30ed9b10c77e633f3b98ad26 to your computer and use it in GitHub Desktop.
Save bruceoutdoors/0685335d30ed9b10c77e633f3b98ad26 to your computer and use it in GitHub Desktop.
Hill Cipher - use via "hill-decrypt.sh" and "hill-encrypt.sh"
#!/bin/bash
. matrix.sh
. mods.sh
. translator.sh
function hill_decrypt {
key=${1// /} # clear spaces from key
if [ ${#key} -lt 4 ];
then
echo "ERROR: Key needs to be 4 characters and above!"
return
fi
if [ ${#key} -gt 4 ];
then
key=${key:0:4} # truncate string to 4 characters
fi
key=($(letters_to_numbers $key))
ciphertext=${2// /} # clear spaces from plaintext
ciphertext=($(letters_to_numbers $ciphertext))
if [ $((${#ciphertext[@]}%2)) -ne 0 ]; then
echo "ERROR: Cipher text length must be even number!!"
return
fi
det=$(getdet ${key[@]})
if ! (( $(hasmatrixinverse $det) ))
then
echo "ERROR: Key has no matrix inverse!!"
return
fi
if ! (( $(hasmodinverse $det) ))
then
echo "ERROR: Key has no modular inverse!!"
return
fi
det=$(mod $det 26)
det=$(frac_mod $det 26)
#~ echo "frac mod determinant is" $det
inverse=( \
$(mod $((${key[3]} * $det)) 26) \
$(mod $((-${key[1]} * $det)) 26) \
$(mod $((-${key[2]} * $det)) 26) \
$(mod $((${key[0]} * $det)) 26) \
)
#~ echo "inverse of the key is" ${inverse[@]}
plaintext=($(multiply ${inverse[@]} ${ciphertext[@]}))
#~ echo "Decrypted numbers: "${plaintext[@]}
echo $(numbers_to_letters ${plaintext[@]})
}
# Hill Cipher Encrypt
# Appends "X" to the end of the input if the input is odd-numbered
function hill_encrypt {
K=${1// /} # clear spaces from key
if [ ${#K} -lt 4 ];
then
echo "ERROR: Key needs to be 4 characters and above!"
return
fi
if [ ${#K} -gt 4 ];
then
K=${K:0:4} # truncate string to 4 characters
fi
K=($(letters_to_numbers $K))
PLAINTEXT=${2// /} # clear spaces from plaintext
det=$(getdet ${K[@]})
if ! (( $(hasmatrixinverse $det) ))
then
echo "ERROR: Key has no matrix inverse!!"
return
fi
if ! (( $(hasmodinverse $det) ))
then
echo "ERROR: Key has no modular inverse!!"
return
fi
if (( ${#PLAINTEXT} % 2 ))
then
PLAINTEXT+=X
fi
P=($(letters_to_numbers "$PLAINTEXT"))
E=($(multiply ${K[@]} ${P[@]}))
echo $(numbers_to_letters ${E[@]})
}
# usage:
# ./hill-decrypt.sh FIRD MIMJSU
# MONDAY
#
#!/bin/bash
. hill-cipher.sh
if (( $# != 2 )); then
echo "ERROR! 2 arguments are required: The key, followed by the cipher text."
echo "Usage: ./hill-decrypt.sh 'FIRD' 'MIMJSU'"
exit 1
fi
echo $(hill_decrypt "$1" "$2")
# usage:
# ./hill-encrypt.sh FIRD MONDAY
# MIMJSU
#
#!/bin/bash
. hill-cipher.sh
if (( $# != 2 )); then
echo "ERROR! 2 arguments are required: The key, followed by the plaintext."
echo "Usage: ./hill-decrypt.sh 'FIRD' 'MONDAY'"
exit 1
fi
echo $(hill_encrypt "$1" "$2")
#!/bin/bash
# Gets the determinant of a 2 x 2 matrix and returns the value
# The matrix is indexed as follows:
# _ _
# | 0 2 |
# |_1 3 _|
#
function getdet {
# local MATRIX=( 5 5 5 5 )
# local MATRIX=( 5 8 17 13 )
local MATRIX=("$@")
det=$(( ${MATRIX[0]} * ${MATRIX[3]} - ${MATRIX[2]} * ${MATRIX[1]} ))
echo "$det"
}
# Matrix multiplication, with ARRAY1[] = 2 x 2 matrix
# ARRAY2[] = 2 x anything matrix
# RESULT[] = Resultant matrix
# The (2 x n) matrices are indexed into arrays as follows:
# _ _
# | 0 2 4 6 ... n-1 |
# |_1 3 5 7 ... n _|
#
function multiply {
# local ARRAY1=( 5 8 17 3 )
# local ARRAY2=( 12 14 13 3 0 24 )
local ARRAY=($@)
ARRAY1=( ${ARRAY[0]} ${ARRAY[1]} ${ARRAY[2]} ${ARRAY[3]} )
#~ echo ${ARRAY1[@]}
COUNT=4 #Start reading from index after key
while [[ $COUNT -lt ${#ARRAY[@]} ]]
do
ARRAY2[$(( $COUNT - 4 ))]=${ARRAY[$COUNT]}
(( COUNT++ ))
done
#~ echo ${ARRAY2[@]}
COUNT=0
while [[ $COUNT -lt ${#ARRAY2[@]} ]]
do
RESULT[$COUNT]=$(( $(( ${ARRAY1[0]} * ${ARRAY2[$COUNT]} + ${ARRAY1[2]} * ${ARRAY2[(( $COUNT + 1 ))]} )) % 26 ))
RESULT[$(( $COUNT + 1 ))]=$(( $(( ${ARRAY1[1]} * ${ARRAY2[$COUNT]} + ${ARRAY1[3]} * ${ARRAY2[(( $COUNT + 1 ))]} )) % 26 ))
COUNT=$(( $COUNT + 2 ))
done
echo "${RESULT[@]}"
}
function hasmatrixinverse {
local DET=$1
if (( $DET != 0 ))
then
echo "1"
else
echo "0"
fi
}
#!/bin/bash
. mods.sh
echo $(mod 0 26)
echo $(mod 27 26)
echo $(mod -121 26)
echo $(frac_mod 9 26)
echo $(frac_mod 7 26)
#!/bin/bash
# mod, factors both negative and positive values
function mod {
local a=$1
local b=$2
if [ $a -ge 0 ]; then # if positive values
while [ $a -ge $b ]; do
let a=a-b
done
else # negative values
while [ $a -le $b ] && [ $a -lt 0 ]; do
let a=a+b
done
fi
echo $a
}
# fractional mod, assuming numerator is 1, and only takes positive fracs
function frac_mod {
local a=$1
local b=$2
local x=-1
local r=-1 # remainder
while [ $r -ne 1 ]; do
let x=x+2
let ax=a*x
r=$(mod $ax $b)
done
echo $x
}
function hasmodinverse {
local DET=$1
if (( $DET % 2 && $DET % 13 )) # 2 and 13 are not relatively prime to 26,
then # and so has no modulo inverse
echo "1"
else
echo "0"
fi
}
#!/bin/bash
. translator.sh
LETTERS=({a..z}) # it will convert to uppercase
for le in "${LETTERS[@]}"
do
echo $(letter_to_number $le)
done
NUMBERS=({0..25})
for nu in "${NUMBERS[@]}"
do
echo $(number_to_letter $nu)
done
#!/bin/bash
function letter_to_number {
local uppercase=$(echo $1 | tr '[a-z]' '[A-Z]')
local ascii=$(LC_CTYPE=C printf '%d' "'$uppercase")
let num=ascii-65
echo $num
}
function number_to_letter {
local LETTERS=({A..Z})
echo ${LETTERS[$1]}
}
function numbers_to_letters {
local letters=( $@ )
for nu in "${letters[@]}"
do
echo -n $(number_to_letter $nu)
done
}
function letters_to_numbers {
for nu in $(seq 1 ${#1})
do
echo -n $(letter_to_number ${1:$nu-1:1})" "
done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment