Random Substitution Cipher (Python) - python

I'm writing a random substitution cipher, and it works well when being used to write regular sentences. However, when I try to test 'abbcccddddeeeee', I will get something back like 'G H H I I I J J J J K K K K K'. How can I change my code so that either 'key' or 'vari' changes for each letter in the cycle yet stays the same so that no letter corresponds to more than one other letter?
import random
intext = raw_input("Enter a message to encrypt: ")
intext = intext.upper()
key = random.randint(0,27)
vari = random.randint(0,27)
def randomsub(intext):
outtext = []
intext = intext.upper()
for c in intext:
num = ord(c)
if num >= ord('A') and num <= ord('Z'):
num = num - ord('A')
num = num + (key + vari) % 26
num += ord('A')
outtext.append(chr(num))
return ' '.join(outtext)
print randomsub(intext)

Not sure why you have both key and vari, but the randomness should be inside your function, and at least one of the calls to randint should be inside the loop. I'd recommend making key a user-provided input, and using it to seed random. That way, with the same text and the same key, you get the same output, but duplicate letters in the text will vary in the result. Last, but not least, if you want the output to be letters you need to expand the parentheses where you add key and vari to include num, i.e., num = (num + vari) % 26 if you switch to using key as I suggested.

Related

Incrementing/decrementing values in a list given specified range (Very green at Python)

I am working on an assignment for an introductory Python class and the objective is to prompt the user for a password, determine it fits the given criteria, then encrypt the password by converting the characters to decimal, incrementing the letters and numbers, and decrementing the characters "!##$" then return the new encrypted password. EX. an input of "123ABCabc$" should output "234BCDbcd#" **EDIT -> fixed this output per #gimix
I have everything up to the step of incrementing/decrementing and this last step has me pulling my hair out. I have tried a gang of different things but the two closest to successful attempts I have are as follows.
-in the first sample new_input is the users original input
-in the second sample use_ascii is new_input converted to decimal
1)
def encrypt(new_input):
encrypted = ''
for char in new_input:
if chr(ord(char)) in list(range(97,123)): #ive tried different formatting options, thus the inconsistency
encrypted += chr(ord(char) + 1)
if chr(ord(char)) > str(64):
encrypted += chr(ord(char) + 1)
if chr(ord(char)) >= str(48):
encrypted += chr(ord(char) + 1)
if chr(ord(char)) == str(64):
encrypted += chr(ord(char) - 1)
if chr(ord(char)) >= str(32):
encrypted += chr(ord(char) - 1)
return encrypted
def encrypt(use_ascii):
encr = ''
list1 = list(range(97,123))
list2 = list(range(48,58))
list3 = list(range(56,91))
list4 = list(range(30,35))
for i in use_ascii:
if i in list1:
encr = i + 1
if i in list2:
encr = i + 1
if i in list3:
use_ascii = i + 1
if i in list4:
use_ascii = i - 1
return encr
then my main statements are (swapping 'use_ascii' for 'new_pass' when i test different options)...
new_input = input('Please input a password: \n')
new_encr = encrypt(use_ascii)
print('Your encrypted password is: {}'.format(encrypt(use_ascii)))
**EDIT per Jasmijn
Sample 1 outputs: just errors and I havn't been trying to fix it as much as I suspect that is the less-correct way to proceed
Sample 2 outputs: Your encrypted password is: 100
This is dec for the letter 'd' which suggests it is selecting the last character which fits the criteria of the first 'if' statement and incrementing it. I need it to increment each character and output the new decimal based on the ranges provided.
The easiest way is to use the isalnum() string method to distinguish between letters/numbers an special characters. So you could simply do:
def encrypt(instring):
outstring = ''
for ch in instring:
outstring += chr(ord(ch)+1) if ch.isalnum() else chr(ord(ch)-1)
return outstring
Or, if you want a one-liner:
def encrypt(instring):
return ''.join((chr(ord(ch)+1) if ch.isalnum() else chr(ord(ch)-1) for ch in instring))

Can you explain me the RLE algorithm code in python

I I've finally found how to make a RLE algorithm by watching a tutorial but This tutorial didn' t explain something in that code I didn't get why we write j = i instead of j = 0 (Knowing that I = 0) it's the same no ?
I didn't get why i = j + 1 either. Why i = j + 1 At the end of the function ? Why not simply i += 1 but if we want to repeat a loop in a loop then we do j + 1 ?
Did the first while loop is supposed to repeat the second while loop until the string is finished ?
And finally why encoded_message is repeated two times ? instead of one. We return encoded_message so that's it ? We can simply do print(encode(text)) instead of
"print('The encoded message is the output ',encoded_message)" (when we put encode(text) into encoded_message)
I know i'm asking a lot of questions but I just can't memorize the code without understanding it, it would be totally useless and unproductive
def encode(message):
encoded_message = ""
i = 0
while(i<len(message)):
count = 1
ch = message[i]
j = i # ???
while(j<len(message)-1): # GET IT -----------------------------------------------------------
if message[j] == message[j+1]: # if the previous and next characters are the same
count = count + 1 # we increase count variable
j += 1 # we increase j position
# GET IT ----------------------------------------------------------------------------
else:
break
encoded_message = encoded_message + str(count) + ch # "" + count converted to string + character (ch)
i = j + 1 # ???
return encoded_message
text = input('enter your charcter chain...')
encoded_message = encode(text)
print('The encoded message is the output ',encoded_message)
When I replaced j = i by j = 0 nothing is displayed in the terminal
see : no result
There is an outer loop and an inner loop. The outer loop with the variable i starts iterating over the message. The inner loop uses the variable j and starts at the current position of i.
That is: when i=0 then j=0. But when i=5 (for example) then j=5 also.
The inner loops task is to check whether 2 or more identical characters follow one another. If they do i is increased accordingly at the end of the inner loop. So that each letter of the message is only looked at once.
That is why j should not be set to a constant value. Setting it to j=0 would cause the inner loop to start at the beginning of the message at every iteration.
I added two simple print() statements to your code to clarify:
def encode(message):
encoded_message = ""
i = 0
while(i<len(message)):
print(f'outer loop: i={i}')
count = 1
ch = message[i]
j = i
while(j<len(message)-1):
print(f'\tinner loop: j={j}')
if message[j] == message[j+1]: # if the previous and next characters are the same
count = count + 1 # we increase count variable
j += 1 # we increase j position
else:
break
encoded_message = encoded_message + str(count) + ch # "" + count converted to string + character (ch)
i = j + 1
return encoded_message
text = 'Hello World'
encoded_message = encode(text)
print('The encoded message is the output ', encoded_message)
(Please note: I do not know the RLE algorithm but just looked at your code.)

The while loop doesn't end but the condition is met

I'm trying to code vigenere cypher so I need to have two lists of the same length. I thought everything will be okay with my solution but the while loop doesn't despite of the fact than condition is met. Because of that I got two list with different numbers of letters. Why this works like that and how to improve that. I am new programmer who learnt from books, so I haven't ever seen that while loop doesn't work properly. My code:
plaintext = "This is secret message"
plaintext = plaintext.upper()
key = "secret"
key = key.upper()
def encrypt_vigenere2( key, plaintext ):
a = []
b= []
i = 0
for letter in (plaintext):
if letter != " ":
a.append(letter)
while i<len(a):
for element in key:
if element != " ":
b.append(element)
i +=1
return a,b
print(encrypt_vigenere2(key,plaintext))
The while loop does as expected, for you now you join the key until you reach the size of a,
b: ['S','E','C','R','E','T','S','E','C','R','E','T','S','E','C','R','E','T','S','E','C','R','E','T']
That will be useful for the next part : encryption
def encrypt_vigenere2(key, plaintext):
...
result = ""
for l, k in zip(a, b):
result += chr(((ord(l) - 65 + ord(k) - 65) % 26) + 65)
return result
You can just use replace(" ", "") to remove the spaces, and to repeat the key, use itertools.cycle
from itertools import cycle
def encrypt_vigenere2(key, plaintext):
result = ""
for l, k in zip(plaintext.replace(" ", ""), cycle(key)):
result += chr(((ord(l) - 65 + ord(k) - 65) % 26) + 65)
return result
The idea for a vigenere encrypter program sounded nice to me so I did some coding and here is my result:
def encrypt_vigenere(plaintext, key):
letter_matrix = [[chr(65 + i) for i in range(26)] for j in range(26)]
for index, column in enumerate(letter_matrix):
for i in range(index):
letter_matrix[index].append(column[0])
del letter_matrix[index][0]
plaintext = plaintext.upper()
key = key.upper()
keystream = ""
ciphertext = ""
current_letter = 0
for letter in plaintext:
if current_letter == len(key):
current_letter = 0
keystream += key[current_letter]
current_letter += 1
for letter_plaintext, letter_keystream in zip(plaintext.replace(" ", ""), keystream):
ciphertext += letter_matrix[ord(letter_plaintext)-65][ord(letter_keystream)-65]
return ciphertext
You said that you were new to coding, so here are the things you might not now:
like previous said, list comprehension
chr() -> takes a number and assigns it to it corresponding ascii value
ord() -> the opposite of chr(). Takes letter and transforms it to ascii number
zip() -> goes trough two lists simultaneously
I am not sure if I understood your code right, but seems for me like you did not try to encrypt something. It looks like you tried to get the keystream, and correct me if I am wrong. But here is the code how to get the keystream:
def encrypt_vigenere3(key, plaintext):
# variables
a = [letter for letter in plaintext.upper() if letter != " "]
b = [element for element in key.upper() if element != " "]
keystream = []
# creating the keystream
i = 0
current_num = 0
while i <= len(a):
if current_num == len(key):
current_num = 0
keystream.append(b[current_num])
current_num += 1
i += 1
return keystream
I created the variables by "list comprehension". You should go look that term up, it makes your code way cleaner.

Python Vigenere working, but I can't account for the spaces and non alphabetical characters using functions

I am currently working on a cipher program for a beginners python course. We first were told to create a function that would return the position of a given letter, using a string of the alphabet as a reference (that is my alphabet_position function.) Next, we were told to make a function that would allow for a single letter to be rotated by a chosen number(that is my rotate_character function). Third, we were tasked with creating a basic caesar cipher using the previous two functions. All of those I was able to make work as demonstrated by my code below.
The vigenere, however, is proving much more difficult. I was actually able to find a snippet of code that I was able to modify with my first function (alphabet_position) to make work if only alphabetic characters are used, but as soon as i enter any non alphabetical character (such as ! or ?) I get a return of ValueError: Substring Not found. When the program encounters these non alphabetical characters, the key is supposed to skip over them and carry the Nth character of the key to the next alphabetical character.
I am guessing the answer lies in somehow incorporating my rotate_character function into my Encrypt function, but I am unsure how to do this, as the rotate_character function expects a alphabetical character, and the vigenere function turns that parameter into an int before running it through.
Any advice? And as I am a new programmer, I will gladly take any other helpful criticism on my coding practices you may want to instill!`
> #Create function alphabet_position(letter) to turn letter into number
> #such as a=0 or e=4, using lowercase to make sure case doesnt matter.
alphabet = "abcdefghijklmnopqrstuvwxyz"
def alphabet_position(letter):
> lower_letter = letter.lower() #Makes any input lowercase.
> return alphabet.index(lower_letter) #Returns the position of input
as a number.
>
> def rotate_character(char, rot):
> if char.isalpha():
> a = alphabet_position(char);
> a = (a + rot) % (int(len(alphabet))); #needs modulo
> a = (alphabet[a]);
> if char.isupper():
> a = a.title()
> return a
> else:
> return char
>
> def caesar(text, rot):
> list1 = ""
> for char in text:
> list1 += rotate_character(char, rot)
> return list1
>
> def vigenere(text,key):
m = len(key)
>
> newList = ""
>
> for i in range(len(text)):
text_position = alphabet_position(text[i])
key_position = alphabet_position(key[i % m])
value = (text_position + key_position) % 26
newList += alphabet[value]
return newList
>
> def main():
> x = input("Type a message: ")
> y = input("Rotate by: ")
> result = vigenere(x, y)
> print (result)
>
> if __name__ == '__main__':
main()
No, you don't need the rotate function anymore. You just need to directly add any character that is not in the alphabet to newlist and then skip the encryption part.
Now a sub-optimal way of doing this is to use if ... in ...:
if text[i] in alphabet:
# do your thing
else:
newList += text[i]
Of course more optimal is to only go through the alphabet once and use a variable:
pt_c = text[i]
pt_i = alphabet.find(pt_c) # returns -1 instead of an error when not found
if pt_i == -1:
newList += pt_c
else:
newList += pt_c
# do your thing *with the given index*
This won't make any difference in the runtime for a Vigenère cipher of course. But it shows you how to think of efficient programming for later: there is no need to search twice.
You could also continue the loop instead of having an else statement:
pt_c = text[i]
pt_i = alphabet.find(pt_c) # returns -1 instead of an error when not found
if pt_i == -1:
continue
# do your thing with the given index
this will make the indentation depth of your loop (the amount of scopes) less, with the unfortunate side effect of making your loop more complex (creating a local exit point).

Error I can't fix while converting text to number in RSA algorithm using Python

I was tasked an assignment by my teacher to make a python program that can encrypt messages using the RSA algorithm. So to do that I had to create functions which convert text to numbers and then be able to manipulate those numbers and encrypt it. However to test if i have encrypted it correctly i also designed a decryption function which I also made at the bottom. All the functions between the encryption functions and Ascii functions are there to be used in other functions(At this point you would realize i like dividing my code into functions). The problem with my code is that i keep getting this error when i run it
Traceback (most recent call last):
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 127, in <module>
print(decrypt(Phi_N, e, encrypted))
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 115, in decrypt
return Num2Text(message)
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 58, in Num2Text
return Ascii2Text(Num2Ascii(message))
File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 17, in Ascii2Text
text = text + chr(int(char))
ValueError: invalid literal for int() with base 10: '1.0'
>>> ================================ RESTART =================
I tested all the functions individually to see if they work and they do. I rechecked my encryption and decryption functions and they should work. For some reason i think my problem is with my decryption function as i think it is giving me the decimal number however i searched the internet on the algorithm and all the sites i looked uses the same algorithm. Please help me with finding the problem, I usually dont ask other people for help (This is my first ever question on StackOverFlow) and I've been hurting my head on this for 3 weeks.
import math
def Text2Ascii(message):
Ascii = ""
for count in range(len(message)):
if len(str(ord(message[count]))) <= 2:
Ascii = Ascii + "0" +str(ord(message[count])) + " "
else:
Ascii = Ascii + str(ord(message[count])) + " "
return Ascii
def Ascii2Text(message):
text = ""
char = ""
for count in range(len(message)):
if message[count] == " ":
text = text + chr(int(char))
char = ""
else:
char = char + message[count]
text = text + chr(int(char))
return text
def Ascii2Num(message):
Num = ""
for count in range(len(message)):
if message[count] != " ":
Num = Num + message[count]
return int(Num)
def Num2Ascii(message):
Ascii = ""
char = ""
message = str(message)
if len(message)%3 == 0:
for count in range(len(message)):
if (count+1)%3 == 0:
char = char + message[count]
Ascii = Ascii + char + " "
char = ""
else:
char = char + message[count]
else:
Ascii = "0"
for count in range(len(message)):
if (count+2)%3 == 0:
char = char + message[count]
Ascii = Ascii + char + " "
char = ""
else:
char = char + message[count]
return Ascii
def Text2Num(message):
return Ascii2Num(Text2Ascii(message))
def Num2Text(message):
return Ascii2Text(Num2Ascii(message))
def exponent(number, power):
answer = 1
for count in range(power):
answer = number*answer
return answer
def primeCheck(number):
if number == 2:
prime = True
elif number <= 1:
prime = False
else:
n = math.sqrt(number)
n = math.ceil(n)
for count in range(2,n+1):
if number%count == 0:
prime = False
break
else:
prime = True
return prime
def factors(number):
answer = []
if primeCheck(number)== True:
answer = [number,1]
else:
for count in range(1,number+1):
if number%count == 0:
answer.append(count)
return answer
def phi(number):
answer = number
if primeCheck(number) == True:
answer = number - 1
else:
factor = factors(number)
for count in factor:
for count2 in factors(count):
if count2 == count:
answer = answer -1
break
return answer
def encrypt(N,e):
message = input("What is the message you need encrypted?\n")
message = Text2Num(message)
encrypted = (message ** e)%N
return encrypted
def decrypt(Phi_N, e,encrypted):
k = 3
d = (Phi_N*k + 1)/e
message = encrypted ** d
return Num2Text(message)
p = int(input("Give me a prime number: "))
while primeCheck(p) == False:
p = int(input("Give me a prime number: "))
q = int(input("Give me a prime number: "))
while primeCheck(q) == False:
q = int(input("Give me a prime number: "))
N = q*p
Phi_N = (q-1)*(p-1)
e = int(input("Enter your encryption key: "))
encrypted = encrypt(N,e)
print(decrypt(Phi_N, e, encrypted))
That specific error that you're getting is due to combination of two factors:
The first, and probably easiest to address, is the way you are breaking up the string in Ascii2Text:
def Ascii2Text(message):
text = ""
char = ""
for count in range(len(message)):
**if message[count] == " ":** #<-- This line is not robust
text = text + chr(int(char))
char = ""
else:
char = char + message[count]
text = text + chr(int(char))
return text
The problem is that, with the way Num2Ascii is implemented, you are ending up with both a trailing space that causes you to try to cast empty string to an int at the end of the loop, and non-space characters earlier in your ascii-fied message. I would recommend either loudly choking on invalid characters, or at the very least not trying to append them to the "char" string that you try to cast to an int on the next line.
This brings us to the next problem: that you even have non-numeral characters in the first place. The decimal point causing the failure you see is coming from an incorrect calculation for d. d is supposed to be the modular multiplicative inverse of e, and it will always be an integer when calculated correctly. Yours, however, was coming up as a decimal more often than not when I ran this code. This is one of the bits of RSA that people choke on most often, since it involves manipulating the modulo operator in ways that people without a math background aren't typically taught. One of the more common, efficient ways to calculate this value is to use the Extended Euclidean algorithm.
A few more notes on things you will probably run into after addressing the two issues above:
e should be co-prime with and less than Phi_N, which you are not currently validating. Failure to do this can make it impossible to correctly calculate d (only few pairs of numbers have a modular multiplicative inverse). The easiest (though not strictly correct) way to do this would be to use a while loop beneath e like you did for the prime numbers, which checks that False == primeCheck(e) or e >= Phi_N. To be more correct, you'd have to write a coprime check that takes in both Phi_N and e, and asserts that they do not share a common factor besides 1.
The padded version of your message, m, must be less than N. Failure to do this may result in some messages being unrecoverable when you attempt to reverse the exponentiation during decryption. To more easily verify this, you may want to capture the message from the user before they select P and Q (they're more likely to change their numbers than the message they want to send), then add an additional gate after the two primes are provided which checks that N > m.

Categories