I realised at some of the shops of a large supermarket chain, that there is a lack of a theft prevention system at the entrance of the shops. I can see how easy it could be just to take something out of the shop without anyone knowing. This is clearly an issue that happens:
Its clearly a system that could be ripe for exploitation.
Another thing I found while at a certain supermarket was that in the auto checkout machines some of the machines were not locked. The receipt machine inside the self checkout machine had run out of paper so a worker came over and just opened up the bottom. This could be exploited easily if given the necessary information. I didn’t try to open it because I didn’t want to be suspicious but its probably very likely that many of these machines are not locked most of the time which could be an incredible risk.
The beginning to figuring out any cipher is to diagnose the internal logic of the cipher. Relatively quickly after the Tunny messages were found, John Tiltman was able to disern that the ciphertexts used the Vernam cipher. This is the first
XOR function
The XORing function itself is not the weakness however depending on implementation and human error it is possible for other people to discover the key used to encrypt the plaintext.
XORing is a symmetric process meaning that the same key to encrypt the messages can be used to decrypt the messages.
Plaintext ⊕ Key = Ciphertext
Ciphertext ⊕ Key = Plaintext
However, if the effect of the key can be cancelled out if 2 messages encrypted with the same key is XORed together. The output will be the same as if both plaintexts were XORed together
If one could work out what Plaintext1 and Plaintext2 are then the key could be found just by:
Plaintext1 ⊕ Ciphertext1 = Key
Plaintext2 ⊕ Ciphertext2 = Key
If the key could be found this would be devastating as people could figure out how the internal logic of the cipher worked.
Human Error (arguably the most impactful)
After a year with little progress a human mistake on the Nazi front allowed cryptanalysts at Bletchley Park to decipher the inner workings of the cipher within a year.
Because the effect of the key can be cancelled out by XORing both ciphertexts it was possible for cryptanalysts at Bletchley Park to find out some of the letters when 2 similar messages were sent (usually due to mistakes). However, most messages were too short to actually assign correct characters to large amounts of the text.
The big mistake was that a German operator sent a long message, nearly 4000 characters long, to the German high command. High command did not receive the message correctly and asked for the message to be sent again. Both Lorenz machines were set back to the starting positions and the message sent again. If the German operator followed protocol and sent the message exactly as it was before then this would have given nothing to the British however, being human, they took some shortcuts and made the message shorter by summarising some unnecessary portions. This produced 2 radically different ciphertext of which was used to crack the internal logic of the cipher.
Lack of significant rotation
On the right side, the chi wheels, did not rotate as much as the phi wheels this resulted in a lack of significant pseudo randomisation which made it easier to crack the messages
This one was a bit harder to do as its clearly taken alot longer (mostly because the logic of my code was not correct 😢). However now it works relatively well it should be able to decrypt most substitution ciphers. From what I see the longer your ciphertext the less variation in the scores outputted at the end. With shorter texts it might not work as well. So below is the code:
import re, random from ngram_class import nGramInfo #substitution cipher def main(): # grabs input and makes it lower case ciphertext = input("Enter the message: ") ciphertext = ciphertext.lower() # removes everything except for letters this is used for calculating the healthiness ciphertext_only_letters = re.sub("[^a-z]+", "", ciphertext) alphabet = list("abcdefghijklmnopqrstuvwxyz") #randomly shuffles the alphabet random.shuffle(alphabet) starter_key = "".join(alphabet) #starter key is random arrangement of alphabet # calculates the best keys and prints them out in a decending order of healthiness # highest healthiness (most correct) will be at the bottom best_keys = calculate_best_key(ciphertext_only_letters, starter_key, ciphertext) best_keys = sorted(best_keys) for i in range(len(best_keys)): print("================\n"+"Score:", best_keys[i][0], "\nKey:", best_keys[i][1], "\nDecryption:", substitution(ciphertext, best_keys[i][1])) # print(best_keys) #prints out the best guess #its just the last thing anyway print("\n======== BEST GUESS ========") print("Score:", best_keys[-1][0]) print("Key:", best_keys[-1][1]) print("Decryption:", substitution(ciphertext, best_keys[-1][1])) def calculate_best_key(ciphertext_only_letters, starter_key, ciphertext): #creates object to calculate fitness calculator = create_nGramInfo_class() print("\n\n\nCiphertext:", ciphertext) print("Ciphertext Healthiness Score:", calculator.calculate_fitness_score(ciphertext)) highest = [] #highest holds the highest scores and the keys associated with the scores # this is the key that is shuffled into a new key every iteration key = list('abcdefghijklmnopqrstuvwxyz') # if the thing cant be solved iterations should be increased first iteration = 0 while iteration < 15: print("Iteration:", iteration) #generates new key and calculates the fitness of the keys associated possible plaintext random.shuffle(key) new_key = "".join(key) print("new key:", new_key) high_score = calculator.calculate_fitness_score(substitution(ciphertext_only_letters, new_key)) best_key = new_key highest.append([high_score, best_key]) iteration += 1 i = 0 # this can be increased if solver not solving however changing iteration number should be first while i < 10000: prev_key = new_key #generates 2 random numbers (musnt be the same) num1 = num2 = 0 while num1 == num2: num1 = random.randint(0, 25) num2 = random.randint(0, 25) #swaps the letters new = list(new_key) new[num1], new[num2] = new[num2], new[num1] new_key = "".join(new) #creates the possible plaintext and calculates fitness plaintext = substitution(ciphertext_only_letters, new_key) new_score = calculator.calculate_fitness_score(plaintext) #if new score is higher than high score new score becomes high score and is added to highest list if new_score > high_score: high_score = new_score best_key = new_key highest.append([high_score, best_key]) if len(highest) > 20: highest = sorted(highest) highest = highest[5:] else: #if score doesnt increase it goes back to previous key new_key = prev_key i += 1 # summarises information in every iteration print("Current highest score is", high_score, "on iteration", iteration) print(best_key) print("This decodes to", substitution(ciphertext_only_letters, best_key)) print("\n\n") return highest def create_nGramInfo_class(): print("Enter '1' for monograms") print("Enter '2' for bigrams") print("Enter '3' for trigrams") print("Enter '4' for quadgrams") print("Note: quadgrams can only do analysis on messages >= 4 characters\ntrigrams for >= 3 and so on \n(if you need a program to help decipher a < 4 letter caesar cipher RIP)") mode = input("Enter Mode: ") mode = mode.strip().lower() if mode == '1': file_name = "ngrams/english_monograms.txt" elif mode == '2': file_name = "ngrams/english_bigrams.txt" elif mode == '3': file_name = "ngrams/english_trigrams.txt" elif mode == '4': file_name = "ngrams/english_quadgrams.txt" else: print("Make sure input is correct") exit() return nGramInfo(file_name) def substitution(ciphertext_only_letters, key): plaintext = "" for letter in ciphertext_only_letters: if letter.isalpha() == True: plaintext += key[ord(letter) - ord("a")] else: plaintext += letter return plaintext if __name__ == "__main__": main()
Here are some tests using it.
Test 1
Message we are encrypting: “short message test”
Key used to encrypt: “qwertyuiopasdfghjklzxcvbnm”
basically just left to right on the keyboard
Ciphertext: “ligkz dtllqut ztlz”
Program ouputs:
Running the program 2 times gives us nothing significant it is mostly just gibberish however imagine someone with more computing power than me they could run multiple versions of this program while increasing the number of iterations the program goes through. It could easily decipher these messages with little issue.
Test 2
Message we are encrypting: “longer messages should work much better with these substitution cipher solvers however i mean who is even trying to solve the shorter ones using a computer”
Guess what it was solved on the first go despite having such a large keyspace it is still relatively easy to crack a substitution cipher
From this I was legitimately surprised at how fast the substitution cipher could be cracked even with the resources I had. As we had to do many cryptograms, which are essentially substitution ciphers, I thought that this would be harder for a computer to do because even for us it would take a decent amount of time to decrypt them. Its shocking how fast a computer can do this just be randomly generating keys and choosing the better ones.
Note: once again i don’t know if the code is visible so will leave it below (all the code is on the github anyway)
import re, random
from ngram_class import nGramInfo
#substitution cipher
def main():
# grabs input and makes it lower case
ciphertext = input("Enter the message: ")
ciphertext = ciphertext.lower()
# removes everything except for letters this is used for calculating the healthiness
ciphertext_only_letters = re.sub("[^a-z]+", "", ciphertext)
alphabet = list("abcdefghijklmnopqrstuvwxyz")
#randomly shuffles the alphabet
random.shuffle(alphabet)
starter_key = "".join(alphabet) #starter key is random arrangement of alphabet
# calculates the best keys and prints them out in a decending order of healthiness
# highest healthiness (most correct) will be at the bottom
best_keys = calculate_best_key(ciphertext_only_letters, starter_key, ciphertext)
best_keys = sorted(best_keys)
for i in range(len(best_keys)):
print("================\n"+"Score:", best_keys[i][0], "\nKey:", best_keys[i][1], "\nDecryption:", substitution(ciphertext, best_keys[i][1]))
# print(best_keys)
#prints out the best guess
#its just the last thing anyway
print("\n======== BEST GUESS ========")
print("Score:", best_keys[-1][0])
print("Key:", best_keys[-1][1])
print("Decryption:", substitution(ciphertext, best_keys[-1][1]))
def calculate_best_key(ciphertext_only_letters, starter_key, ciphertext):
#creates object to calculate fitness
calculator = create_nGramInfo_class()
print("\n\n\nCiphertext:", ciphertext)
print("Ciphertext Healthiness Score:", calculator.calculate_fitness_score(ciphertext))
highest = [] #highest holds the highest scores and the keys associated with the scores
# this is the key that is shuffled into a new key every iteration
key = list('abcdefghijklmnopqrstuvwxyz')
# if the thing cant be solved iterations should be increased first
iteration = 0
while iteration < 15:
print("Iteration:", iteration)
#generates new key and calculates the fitness of the keys associated possible plaintext
random.shuffle(key)
new_key = "".join(key)
print("new key:", new_key)
high_score = calculator.calculate_fitness_score(substitution(ciphertext_only_letters, new_key))
best_key = new_key
highest.append([high_score, best_key])
iteration += 1
i = 0
# this can be increased if solver not solving however changing iteration number should be first
while i < 10000:
prev_key = new_key
#generates 2 random numbers (musnt be the same)
num1 = num2 = 0
while num1 == num2:
num1 = random.randint(0, 25)
num2 = random.randint(0, 25)
#swaps the letters
new = list(new_key)
new[num1], new[num2] = new[num2], new[num1]
new_key = "".join(new)
#creates the possible plaintext and calculates fitness
plaintext = substitution(ciphertext_only_letters, new_key)
new_score = calculator.calculate_fitness_score(plaintext)
#if new score is higher than high score new score becomes high score and is added to highest list
if new_score > high_score:
high_score = new_score
best_key = new_key
highest.append([high_score, best_key])
if len(highest) > 20:
highest = sorted(highest)
highest = highest[5:]
else: #if score doesnt increase it goes back to previous key
new_key = prev_key
i += 1
# summarises information in every iteration
print("Current highest score is", high_score, "on iteration", iteration)
print(best_key)
print("This decodes to", substitution(ciphertext_only_letters, best_key))
print("\n\n")
return highest
def create_nGramInfo_class():
print("Enter '1' for monograms")
print("Enter '2' for bigrams")
print("Enter '3' for trigrams")
print("Enter '4' for quadgrams")
print("Note: quadgrams can only do analysis on messages >= 4 characters\ntrigrams for >= 3 and so on \n(if you need a program to help decipher a < 4 letter caesar cipher RIP)")
mode = input("Enter Mode: ")
mode = mode.strip().lower()
def substitution(ciphertext_only_letters, key):
plaintext = ""
for letter in ciphertext_only_letters:
if letter.isalpha() == True:
plaintext += key[ord(letter) - ord("a")]
else:
plaintext += letter
Arriving at UNSW from the front, despite doing so many times before this instance I saw the bollards and though about how they provided safety and security for people on the walkway. Bollards are these poles/structures that are placed in the ground to prevent cars or other large vehicles from getting through. I don’t know when these were installed but I am speculating that one of the reasons could be terrorist attacks involving cars.
Obviously I do not know if they had this in mind when they were installed and it could be pure speculation however it is not unlikely. There are places around the world where bollards have been installed just to prevent terrorist attacks using cars. Examples:
I knew that bollards are used to increase the safety of pedestrians however it was still a bit jarring thinking of the reasons the bollards could be there. I guess its because normally its something so innocuous and you never notice it but yet is still a security feature.
However, in practice it is better to keep the number of “on” cams and the number of “off” cams around the same or else there could be long sections of “on” bits and “off” bits which is a cryptographic weakness.
Therefore, there would be approximately 501C250 + 501C251 different positions
The total will be the product of the last 2 numbers which will result in another enormous number
The thing is its just a large number of starting positions making it impossible to break through a brute force attack
Wheels have co-prime number of positions
Notice number of positions on different wheels are all co prime to other wheels.
This provides the longest possible time before patterns are repeated. This makes it more difficult to crack through analysing repeating patterns (like in Vigenère)
Used physical wires or directed radio to communicate
This made it harder to intercept the messages, particularly during the war where people were actively trying to prevent people coming into their respective countries.
For the radio signals these directed at the next receiver hence in Britain the signals were very weak.
As missing a single missing or incorrect character could make decryption impossible it required the British to employ some 600 employees just to get interpret the signals properly.
Using XOR function and a pseudo random number generator
A combination of these 2 makes it incredible hard to decipher the message provided that the number generator is highly random.
This combination makes the ciphertext invulnerable to frequency analysis.
Overall the cipher is a very safe cipher as long as the only you and the people you want to send the messages to have knowledge of the intricate workings of the cipher. After the British detected the first messages from the Lorenz cipher, they made little progress on deciphering the ciphers for almost a year. It wasn’t until a human error that spelt the end the beginning of the end for this cipher.
The Lorenz cipher was run on machines as the machines could generate pseudorandom keys based on different starting positions. These types of ciphers were called mechanical ciphers where machines/mechanical structures are used in the process of enciphering and deciphering.
This above is a Lorenz machine for reference.
The keys are generated by the 12 wheels in the machine as seen above. Depending on the starting positions each following character entered will be enciphered differently. The 5 wheels on the right were called the “chi” wheels, the 5 wheels on the left are the “psi” wheels and the remaining 2 were the “mu” keys. When entering a new character to encipher the chi wheels always rotated by 1 position however the psi wheels didn’t need to. Note each wheel had a different number of positions to provide greater complexity.
Pic from Wikipedia
On each wheel, at each number was a little cam (a pin like switch)
The cams can be raised or lowered
On the chi and phi wheels when the cam is raised it will reverse the value of the bit
Which creates more randomness and complexity
On the larger mu cipher the cams control the rotation of the smaller mu cipher which in turn controls the rotations of the phi wheels.
Baudot Code
As the Lorenz cipher is a teleprinter cipher machine which usually had its communication on wires it used the Baudot code.
The Baudot code was the predecessor to ASCII code, the alphabet contained 32 symbols.
The output of the code was in 5 channels each of which had a stream of holes and no-holes (1s and 0s)
Enciphering
The key is generated by XORing the phi and chi wheels. To create the ciphertext/plaintext the key is XORed with the plaintext/ciphertext
As the wheels rotated in accordance to the cans the key will change for every single letter hence makes this a incredibly strong cipher.
The Lorenz cipher was implemented using a Vernam cipher.
It was created in 1918 (decades before WWII) in America.
The Vernam cipher is a symmetrical stream cipher in which the plaintext is combined with a pseudo randomly generated key using the Boolean exclusive or (XOR) function. If the key was truly random and used only once this becomes a one-time pad.
The exclusive or function (symbolised using ⊕) operates as such (where 0 is false and 1 is true):
0 ⊕ 0 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
1 ⊕ 1 = 0
Basically, it outputs true if only one of the inputs is true.
A cipher is symmetric when the same key can be used for encryption and decryption. In the Vernam cipher, with the key you can XOR the plaintext to produce the ciphertext, this ciphertext can be XORed again to produce the original plaintext. (as shown below)
Plaintext ⊕ Key = Ciphertext
Ciphertext ⊕ Key = Plaintext
A stream cipher is a cipher in which the plaintext and the key are combined using an algorithm one bit at a time. This directly contrasts with a block cipher where the key and algorithm is applied to blocks of data rather than individual bits.
Originally Vernam’s idea was to use the standard telegraphy practice of using paper tapes to store the keys. Each key tape was supposed to be unique (which in turn would have become a one-time pad) however the logistics of creating and distributing these tapes prevented this idea from being implemented instead rotor cipher machines were created to produce a pseudo-random key.
The Lorenz cipher and the Enigma Cipher were the 2 major ciphers used by the German army during WWII. The Enigma machine is the more commonly known encryption device used by the Nazis because it was used for a majority of German Army communications. The enigma machine was used for general military communication; the Lorenz cipher was only used by Hitler, his high command and top generals.
A crucial difference between the Enigma cipher and the Lorenz cipher was that enigma had its messages sent via radio by means of Morse code, however the Lorenz cipher had its messages sent using teleprinter signals (telex). This made it significantly harder to intercept the messages sent by the Lorenz cipher, without many messages sent using the cipher made decrypting the messages much harder.
The Lorenz cipher operated on 3 different machine models: Lorenz SZ40; Lorenz SZ42a; Lorenz SZ42b. They each brought into use in June 1941, February 1943, and June 1944 respectively. Newer revisions improved upon the older models. Because these machines were used by only the upper echelon of the Nazi command there were very few machines ever build
The cipher was broken by the codebreakers at Bletchley Park (central site for codebreakers during WWII, same place where enigma code was broken) using Colossus. Colossus was the world’s first electronic, programmable computer. The work done by the codebreakers at Bletchley Park and the existence of Colossus was kept from the public eye until the mid-1970s
During the War British cryptanalysts referred to encrypted German communications as Fish. They designated the Lorenz machine and its communications as Tunny (Tuna fish). Despite how hard the cipher is to crack using brute force methods because of human error codebreakers at Bletchley Park were able to break the cipher without ever seeing the machine itself.
Government Data Collection or Right To Privacy (Case Study)
This week we were thinking about this:
Should the government or government agencies collect and have access to your data for good purposes, or should citizens, e.g. you, have a right to privacy which stops them?
The class was split into 2 groups one arguing for more government data collection (alas a losing side that I was on) and another arguing for the right to privacy.
Government Data Collection
Increases security and safety of public
An increased collection of data could allow crimes to be solved faster and more efficiently
This could act as a deterrence for to be criminals
It could prevent more terrorist attacks
Could target all types of crime including white collar crime
Information can be kept only for a certain period of time after which governments require a reason of which they have to appeal to the judiciary
This will make the proposal seem more reasonable
Reduces the amount of data that could be released and leaked
Mitigates the amount of damage
Heavy surveillance will be for suspicious people on certain lists
This will place resources where they are needed
People who have nothing to hide have nothing to fear
(despite how bad this excuse is people do believe it)
Allows the government and government agencies and to provide better services
With large amounts of data the government and government agencies can better decide where to allocate its resources to help create a more equal society
Improves government decision making for infrastructure, healthcare, etc.
For example if the data shows a trend in increased diabetes or something similar, a government could increase funding for research and treatment into those specific diseases
Reduce inefficiencies and redundancies within government services
For example if they found that less people were using a specific service they could reduce funding for that specific service
Right to Privacy
Creates single point of failure
Increases the effectiveness of insider attacks
could be used by enemy state agents to access large amounts of data
this could risk the welfare and safety of the Australian public
Increases the consequences of a data hack or leak
If data were to be leaked the damage caused could be incredibly significant
Imagine if My Health Record was released and companies could discriminate against people with long term conditions
Identity theft, price gouging, social scrutiny are all consequences of a data leak (in particular from sensitive data that the government could hold)
Risk of future governments with malicious intentions
The government implementing the legisltion may have good intentions and is willing to use the powers reasonably. This however does not prevent any future government from doing the same.
A future government could sell off the information to select companies to generate a quick buck, they could increase the powers and remove previously imposed restraints and take advantage the established system, an individual within the government could use the system for their own benefit without the knowledge of others.
This list is not exhaustive so much wrong can come from implementing such policies
In this we see that a facial recognition trial in London fails 80% of the time. Something with such a high failure rate is unacceptable anywhere
Because of technical issues and the lack of necessary training data, facial recognition technology works less effectively for minority ethnic groups
This could inadvertently place innocent people in positions where they are accused of crimes they never committed
There are many more arguments for and against each side. However this question on whether there should be more data collection or a better regard to the right to privacy is pretty cut and dry from a security perspective. Increasing data collection unnecessarily increases the risks for little gain. The ease at which a system could be abused is frightening. It is naive to believe that people can create a system that only lets the “good“ guys in while repelling the “bad“. In Australia new laws that allow the government to force a software developer to implement a backdoor into their software is a overreach of power and is not acceptable when we consider how much information could be put at risk. Perhaps a reasonable middle ground can exist in this debate but given the 2 choices it is better to err on the side of privacy.
Like most other poly alphabetic substitution ciphers the main idea was to create a cipher that will disguise letter frequency which greatly interfered frequency analysis methods
Largely uncrackable without knowledge of methods
Without knowing the Kasiski method or the Friedman test it is incredibly difficult to find the key other than through brute force methods
Literally unbroken for 300 years
Large key space
The key space is 26k where k is the length of the key
With just a key of length 10 you would reach 141167095653376 different keys
Weaknesses
Repeating nature of the key (largest weakness that leads to other weaknesses)
Because the key repeats it makes it much easier to guess the length of the key. Using Kasiski examination and the Friedman test the length of the key can be found much faster than brute force methods
One could just go a bit more and use a one time pad or a running key cipher (basically a Vigenère cipher however the key is longer than the message, usually a sentence from a book or something similar)
Kasiski Examination
This is a method of attacking poly alphabetic substitution ciphers
Published by Friedrich Kasiski in 1863 however also independently discovered by Charles Babbage in 1846
This method involves looking for strings of characters that are repeated in the ciphertext. The distance between these repeated strings will likely give you a multiple of the length of the key.
Finding more of these repeated strings will narrow down the range of the possible lengths of the key as we can find the grates common divisor of the distances
An example taken from Wikipedia
abcdefabcdefabcdefabcdefabcdefabc
crypto is short for cryptography.
In this we can see that word crypto doesn’t line up with both “abcdef”s
This tells us that the key length is unlikely to be a multiple of 6
abcdeabcdeabcdeabcdeabcdeabcdeabc
crypto is short for cryptography.
In this the word crypto lines up with “abcdefa” both times this makes it likely for the key length to be a multiple of 5
This method works better with longer strings
Once you find out the length of the key you can now just split the ciphertext into the lengths of the key and place them in a column each column of the ciphertext can be treated as a monoalphabetic substitution cipher which you can solve through frequency analysis.
Friedman Test
Friedman test (also known as the kappa test) uses the fact that the unevenness of letter distribution due to the polyalphabetic substitution cipher will create a different index of coincidence from what is to be expected from normal English (other languages work as well)
This above will give you the approx. key length
Where kp is probability of 2 randomly chosen letters are the same (0.067 for English)
Where kr is the probability of a coincidence for a uniform random selection from the alphabet (1/26 in English)
Images from Wikipedia
This method is an approximation only and increases in accuracy the longer the size of the text is.
The Vigenère cipher is a polyalphabetic substitution cipher originally described by Giovan Battista Bellaso in the year 1553. The cipher however was misattributed to Blaise de Vigenère in the 19th century, the name stuck so now it is commonly called the Vigenère Cipher. Blaise de Vigenère actually created a different cipher (though pretty similar to the Vigenère Cipher) called the autokey cipher.
It was called ‘le chiffre indéchiffrable’ which was French for “the indecipherable cipher”. It was left unbroken for 300 years until Friedrich Kasiski described a general method of deciphering the cipher in 1863.
The first mentions of a poly alphabetic substitution cipher were made by Al-Qalqashandi during the 14th to 15th century. However the first well documented poly alphabetic substitution cipher is the Alberti cipher created by Lean Battista Alberti. The invention of the Alberti cipher revolutionised encryption, being the first polyalphabetic substitution, it was a new method of encrypting a message that could mask frequency distributions of the letters. As frequency analysis was the only known technique at the time for attacking ciphers it largely prevented anyone cracking the cipher.
The Vigenère cipher is the most well-known poly alphabetic substitution cipher, it stood for a long time without being broken yet it has. Nowadays the Vigenère cipher is not used in any serious cryptographic setting but it can still be used for less serious purposes.
I implemented this cipher with both encrypt and decrypt functionality. It is a symmetric cipher hence it was very easy to include both (just had to change 2 signs)
#vignere cipher #i guess also kinda one time pad #getting message input and converting to lowercase plaintext = input("Enter the message: ") plaintext = plaintext.lower().strip() #get the key for the vignere cipher print("Enter a sequence of letters for key") key = input("Enter the key: ") key = key.lower().strip() # checking if key is all alphabetical if key.isalpha() == False: print("\nMake sure your key only contains letters") exit() # making sure key is at least as long as plaintext key = key * int((len(plaintext) / len(key)) + 1) ciphertext = "" mode = input("Enter 'e' for encryption and 'd' for decryption") mode = mode.lower().strip() if len(mode) != 1 or mode.isalpha() == False: print("Make sure input is correct") exit() if mode == 'e': #the encryption key_iter = 0 for i in range(len(plaintext)): if plaintext[i].isalpha(): new_letter = ord(plaintext[i]) + ord(key[key_iter]) - ord('a') if new_letter > ord("z"): new_letter -= 26 ciphertext += chr(new_letter) key_iter += 1 else: ciphertext += plaintext[i] elif mode == 'd': # the decryption key_iter = 0 for i in range(len(plaintext)): if plaintext[i].isalpha(): new_letter = ord(plaintext[i]) - ord(key[key_iter]) + ord('a') if new_letter < ord("a"): new_letter += 26 ciphertext += chr(new_letter) key_iter += 1 else: ciphertext += plaintext[i] else: print("Make sure only 'e' or 'd' is inputted") exit() print("Your message:", ciphertext)
The Vigenère cipher is a poly alphabetic substitution cipher. A basic description would be that it is using a different Caesar cipher on each letter.
The key (to encode and decode the message) for this cipher is a word, it can be any word (even just a randomly generated sequence). The key should be less than the length of the message because if the key is longer than the length the cipher will become a one-time pad, which is an unbreakable cipher.
Let’s explain the cipher using an example:
So, let’s just say the key is:
field
And let’s say the plaintext you want to send is:
makingmywaydowntown
To encode the message you first need to make the key as long as the message. As we can see the key is only 5 letters long so we just repeat it until it is long enough.
fieldfieldfieldfiel
You then need to add the letters from the plaintext with the corresponding letters from the key and subtract one
The cipher text will be:
fieldfieldfieldfiel (key)
makingmywaydowntown (plaintext)
riotqluchddlshqyway (ciphertext)
So to get the first letter of the cipher text we add the first letter of the plaintext M (13th letter) to the first letter of the key f (6th letter). This gives us 19.
We then decrement 19 by one (just how the cipher works), giving us 18. That corresponds to the letter R
We then repeat this process with every letter in the plaintext giving us the cipher text
This process can be made easier using a Vigenère square/table as shown below.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z --------------------------------------------------- A A B C D E F G H I J K L M N O P Q R S T U V W X Y Z B B C D E F G H I J K L M N O P Q R S T U V W X Y Z A C C D E F G H I J K L M N O P Q R S T U V W X Y Z A B D D E F G H I J K L M N O P Q R S T U V W X Y Z A B C E E F G H I J K L M N O P Q R S T U V W X Y Z A B C D F F G H I J K L M N O P Q R S T U V W X Y Z A B C D E G G H I J K L M N O P Q R S T U V W X Y Z A B C D E F H H I J K L M N O P Q R S T U V W X Y Z A B C D E F G I I J K L M N O P Q R S T U V W X Y Z A B C D E F G H J J K L M N O P Q R S T U V W X Y Z A B C D E F G H I K K L M N O P Q R S T U V W X Y Z A B C D E F G H I J L L M N O P Q R S T U V W X Y Z A B C D E F G H I J K M M N O P Q R S T U V W X Y Z A B C D E F G H I J K L N N O P Q R S T U V W X Y Z A B C D E F G H I J K L M O O P Q R S T U V W X Y Z A B C D E F G H I J K L M N P P Q R S T U V W X Y Z A B C D E F G H I J K L M N O Q Q R S T U V W X Y Z A B C D E F G H I J K L M N O P R R S T U V W X Y Z A B C D E F G H I J K L M N O P Q S S T U V W X Y Z A B C D E F G H I J K L M N O P Q R T T U V W X Y Z A B C D E F G H I J K L M N O P Q R S U U V W X Y Z A B C D E F G H I J K L M N O P Q R S T V V W X Y Z A B C D E F G H I J K L M N O P Q R S T U W W X Y Z A B C D E F G H I J K L M N O P Q R S T U V X X Y Z A B C D E F G H I J K L M N O P Q R S T U V W Y Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Z Z A B C D E F G H I J K L M N O P Q R S T U V W X Y
To use the square you just match up the letters of the key and plaintext. So using the same example, we just go to the row with the letter f (first letter of key) then we look for the column with m (first letter of the plaintext), we see that this gives us the letter R. This will give us the same ciphertext.
The first solver that I wrote used a dictionary of words, however this required the words in the cipher to be seperated by a space or else it would not work. This obviously is not very useful when the ciphertext is just one long string of letters. So using frequency analysis, more accurately the frequency of groups of letters I have written a program that can work without spaces.
The frequency analysis resources and ideas come from: http://practicalcryptography.com/cryptanalysis/stochastic-searching/cryptanalysis-caesar-cipher/
#caesar cipher solver frequency analysis (coincidence index) from ngram_class import nGramInfo import re def main(): # grabs input and converts it to lowercase ciphertext = input("Enter the message: ") ciphertext = ciphertext.lower() # list of all the possible shift values all_shifts = [] # creates all possible messages and adds it to list for shift in range(26): plaintext = '' for letter in ciphertext: if letter.isalpha(): new_letter = ord(letter) + shift if new_letter > ord('z'): new_letter -= 26 plaintext += chr(new_letter) else: plaintext += letter all_shifts.append(plaintext) # creates a dictionary for fitness scores of all shifts fitness_scores = {} # you can choose between how many letters you want to group together print("Enter '1' for monograms") print("Enter '2' for bigrams") print("Enter '3' for trigrams") print("Enter '4' for quadgrams") print("Note: quadgrams can only do analysis on messages >= 4 characters\ntrigrams for >= 3 and so on \n(if you need a program to help decipher a < 4 letter caesar cipher RIP)") mode = input("Enter Mode: ") mode = mode.strip().lower() if mode == '1': file_name = "ngrams/english_monograms.txt" elif mode == '2': file_name = "ngrams/english_bigrams.txt" elif mode == '3': file_name = "ngrams/english_trigrams.txt" elif mode == '4': file_name = "ngrams/english_quadgrams.txt" else: print("Make sure input is correct") exit() # this creates an object that will do calculations for healthiness scores calculator = nGramInfo(file_name) for i in range(26): temp = re.sub("[^a-zA-Z']+", "", all_shifts[i]) fitness_scores[all_shifts[i]] = [calculator.calculate_fitness_score(temp), i] # sort fitness scores by healthiness sorted_fitness_scores = sorted(((value, key) for (key, value) in fitness_scores.items()), reverse=True) print("\nBelow are the 26 shifts of the cipher ordered by most likely the message\n") print(" Message | Score | Shift") for i in range(26): print(">{} | {:>4.15f} | {}".format(sorted_fitness_scores[i][1], sorted_fitness_scores[i][0][0], sorted_fitness_scores[i][0][1])) if __name__ == "__main__": main()
This is a view of its outputs:
as you can see the best option is printed at the top, it is judged by a score of how healthy the text is. it basically just counts how often certain groups of letters appear and adds up the totals. (the groups of letters all have scores depending on how common they are)
Suppose we are thrust into a war with a superpower such as Russia.
What sorts of computer and internet related attacks might we suffer?
Australia will most likely defend and coordinate attack plans with other allied countries
The most important assets that we need to protect include:
Power systems
knocking out the power systems will cause incredible disarray within the country, it will prevent information from spreading as fast therefore slowing private and government response to a physical attack to the country.
Overall this is crippling to the country. This happened partially in the city of Kiev, Ukraine
https://www.bbc.com/news/technology-44937787
last paragraph
Transport systems
preventing people from moving around along with removing power will cripple the economy and slowing down the food transportation to starve the country
Online government infrastructure
keeping government services running will reduce disruption to a minimum
Banking data
getting access into the banking systems could disrupt or destroy the economy and with information from the data you could
Unity of the country and allies
keeping the country unified will keep disruptions to a minimum
What recommendations should be implemented
Separate military (government) and civilian resources
Electricity, communication, transport, etc. these should be separated and use different systems to prevent both failing at the same time
decentralize power networks
through the use of renewable energy and batteries to create a decentralized power network only parts of the system can go down and are not controlled by a single entity
discuss important things in person
it makes it less likely for someone to snoop using technology if talks are done in secure places in person
plan and implement measures to cause chaos within their systems
basically we can prepare to do everything they are doing to us back at them
attack electricity grid, transport network, infrastructure, etc.
Strengthen security legislation
To force companies and government departments to implement better security conduct and procedures, use secure and new cryptographic systems (and definitely not forcing companies to include back doors into their code)
Use of physical switches
things that turn off internet access, electricity access, etc. should all have physical switches so it is not possible to bypass the block through changes to code
Cyber Draft
this is something like a military draft, however it will recruit individuals with knowledge about computer science/ security to help protect the country from offensive attacks from a cyber medium
From this exercise we can see that there are many ways to cause absolute havoc on another country. In times where people can prepare for years it is important to prepare in advance. Malware and physical devices can be planted in important infrastructure so that they can be activated when the time arises.
Protecting the most centralised systems should be a top priority and a transition to a less centralised system will be beneficial for everyone.
Chose injection attacks from OWASP top 10 vulnerabilities
An injection attack is an attack where external code can be put into the program in order to execute commands to read and modify data within the system.
There are a few different types of injection attacks
SQL
SQL (Structured Query Language) is a language used for database management systems
SQL injections involve people attempting to inject malicious SQL statements. These statements can be used to bypass security measures, perhaps even retrieve the content of the entire database.
To perform an attack, one must first find user inputs within a webpage or web application that is vulnerable. A user input is vulnerable when it is possible to use the input to modify code; usually through the lack of filtering for escape characters or when input is not strongly typed and is executed differently.
To prevent these attacks, you must make sure that all input is parsed effectively and ignored unnecessary information. The direct input should never be used by the program (basically scan the input first)
Buffer overflows
Buffer attacks occur when an attacker maliciously puts more information in a buffer than there is space for it. This allows the attacker to overwrite information past the end of the buffer which allows someone to escalate their privileges.
To prevent attacks by this we could include a stack canary. This is analogous to a canary in a coal mine. It is a small integer placed at the beginning of each stack frame and as buffer overflows usually overwrite memory from lower to higher memory addresses so in order to overwrite the return pointer you need to erase the canary therefore telling the program that something has gone wrong.
Other ways include the use of bounds checking, by writing the program such that it is impossible to go past the end of a buffer.