Hey guys so I 'm trying to make a cipher following these sets of instructions:
Print a header.
Prompt the user to enter the name of the file with the encrypted message, the decode
key (the shift number), and the name of the file to store the decrypted message.
Read the encrypted message from the file.
Use the decode key to shift each character in the encrypted message by the
appropriate number to generate the new string corresponding to the decrypted message.
Save the decrypted message in the second file.
Print the encypted and decrypted messages on the screen.
I'm not allowed to use the ord() or chr() functions.
What really confuses me is the encrypted and decrypted files part. I don't really know how to code for this.
I'm pretty new to this so any help would be greatly appreciated.
Thanks in advance.
Note: It sounds like you're probably doing this as a school assignment. I highly recommend that you use the code below only as an example and not as a full solution. I would hate for there to be plagiarism issues surrounding your assignment and I'm sure your professor/teacher is knowledgeable at Googling for prior work. Good luck on your assignment!
I wrote a quick example of how I might try and tackle your problem. The example has a few known issues:
It doesn't deal with capital letters. (Other than to convert them to their lowercase counterparts.)
It doesn't deal with punctuation or non alphanumeric characters. (Numbers, spaces or line endings.)
There is no error checking.
If you try to convert a number < -25 it will throw up on you.
Probably the biggest problem that needed to be solved was the limitation of not using ord() and chr(). I bypassed that limitation by creating my own conversion list of letters to numbers and vice versa. A tricky corner case to make sure you deal with is what happens if the shift moves a letter outside of the conversion range [0,25].
As a side note if you want to decrypt a file you can simply open it up as the plaintext and use a negative offset whose absolute value is equal to the encrypting offset. Or in plain English, if you use the parameters:
infile = clear.txt, offset = 1, outfile = encrypted.txt
To decrypt you can use:
infile = encrypted.txt, offset = -1, outfile = decrypted.txt
caesarcipher.py
import itertools
letters = ['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']
numbers = range(26) # Numbers 0 - 25
lettersToNumbers = dict(zip(letters, numbers))
numbersToLetters = dict(zip(numbers, letters))
def printHeader():
""" Print the program informational header """
print """=======================================
Welcome to CaesarCipher - The unbreakable
Roman cipher.
======================================="""
def convertToNumber(letter):
""" Convert a letter to a number using our predefined conversion table
#param letter: The letter to convert to an integer value
#type letter: str
#rtype: int
"""
return lettersToNumbers[letter]
def convertToLetter(number):
""" Convert a number to a letter using our predefined conversion table
#param number: The number to convert to a letter
#type number: int
#rtype: str
"""
# If we shift outside of our range make sure to wrap
if number > 25:
return numbersToLetters[number%25]
elif number < 0:
return numbersToLetters[number+25]
else:
return numbersToLetters[number]
def shiftUp(letter, shift):
""" Shift letter up a given number of positions
#param letter: The letter we're shifting
#param shift: The number of positions to shift up
#type letter: str
#type shift: int
#note: For simplicity we encode both capital and lowercase letters
to the same values
"""
number = convertToNumber(letter.lower())
number += shift
return convertToLetter(number)
def prompt():
""" Prompt for user input
#rtype: tuple of str, int, str
"""
infile = raw_input("File to encrypt: ")
offset = int(raw_input("Encoding number: "))
outfile = raw_input("Encrypted file destination: ")
return (infile, offset, outfile)
def encrypt(infile, offset, outfile):
""" Encrypt the file using the given offset """
print "=== Plaintext input ==="
printFile(infile)
with open(infile) as red_file:
with open(outfile, 'w') as black_file:
for line in red_file:
for letter in line:
# Only convert alphabetic characters
if letter.isalpha():
black_file.write(shiftUp(letter, offset))
else:
black_file.write(letter)
print "=== Ciphertext output ==="
printFile(outfile)
def printFile(path):
""" Print the data in the given file """
with open(path) as print_file:
for line in print_file:
print line
printHeader()
encrypt(*prompt()) # `*` unpacks the tuple returned by `prompt()` into
# three separate arguments.
test.txt
abcdef
ABCDEF
This is some text I want to try and encrypt.
Example run:
mike#test:~$ python caesarcipher.py
=======================================
Welcome to CaesarCipher - The unbreakable
Roman cipher.
=======================================
File to encrypt: test.txt
Encoding number: 1
Encrypted file destination: test.out
=== Plaintext input ===
abcdef
ABCDEF
This is some text I want to try and encrypt.
=== Ciphertext output ===
bcdefg
bcdefg
uijt jt tpnf ufyu j xbou up usz boe fodszqu.
Since you say the file bits is your biggest problem, I assume function like:
def decaesar(message, shift):
pass
that does the decyphering for you on a string basis - that is, it takes the encrypted message as a string and gives you back the decrypted message as a string. If you haven't written that already, do that first, and test it with hard-coded strings. Ignore the "encrypted and decrypted files" bit at this stage - programming is all about solving one problem at a time.
Once you have that function and you're happy that it works, extending your program to deal with files instead of strings is as simple as asking:
Can I get a string with the contents of a file, given the file's name? , and conversely,
Can I write a string into a file with a given name?
If you can answer both of those with 'yes', then you can extend your program in this way without changing your decaesar function - your logic looks like this:
# Print header
encrypted_filename, decrypted_filename, shift = # get from user input
encrypted_message = # get the contents of encrypted_filename as a string
decrypted_message = decaesar(encrypted_message, shift)
# write decrypted_message to decrypted_filename
# print encrypted_message and decrypted_message
Usefully, Python's file IO works on exactly this principle of converting between strings and files. If you have a file open for reading:
in_file = open(filename)
, then the return value of:
in_file.read()
is exactly the string to answer the first point. Likewise, if you have a file open for writing:
out_file = open(filename, 'w')
. then:
out_file.write(my_string)
will put my_string into that file.
So that means that if you do already have your decaeser function, then you can slip this code into the pseudocode above at the appropriate places, and you will have a mostly working solution.
Related
Caesar Cipher is a simple way of encryption that shifts a character a number of places in front of it. For example, ABC with a Rotation of 1 would be BCD. In this program, however, the list of all characters include special characters in a random order, i.e.
1234567890-=qwertyuiopasdfghjklzxcvbnm,./~!##$%^&*()_+|\\{}[]:;\'"QWERTYUIOPASDFGHJKLZXCVBNM
Thus, it is more of a custom cipher but following the principle of Caesar Cipher, the code can be tested by using the 2 functions I have made (encode() and decode()) on the same text, and if the output given is the same as the input, it means that it works. I get an output for some rotation numbers, but some numbers, like 70, give me an error. The code I have written is:
characters = '`1234567890-=qwertyuiopasdfghjklzxcvbnm,./~!##$%^&*()_+|\\{}[]:;\'"QWERTYUIOPASDFGHJKLZXCVBNM' # All characters in a string, no specific order
key_raw = 70 # One of the keys that gives me an error.
def encode(text, key): # Function that takes in two inputs: Text and key, which is the number of characters to shift forward
output, text = '', str(text)
limit = len(characters)
while key > limit:
key -= limit # This is my attempt to simplify the key
for i in text:
if i in characters:
output += characters[characters.index(i)+key] # If i is in characters, the rotated character is concatenated to the output
else:
output += i # Otherwise, it directly adds the text
return output
def decode(text, key): # Same thing, except key is subtracted from the index of i in key, as to decrypt it
output, text = '', str(text)
limit = len(characters)
while key > limit:
key -= limit
for i in text:
if i in characters:
output += characters[characters.index(i)-key]
else:
output += i
return output
print(encode('Why do I get String_Index_Out_Of_Range?', key_raw))
Please let me know where I made an error.
Consider what happens when you receive characters.index(i)+key where index is the last symbol in characters.
Simply put, your Caesar cipher is missing modulo operations.
But, to make this more useful, we can still try to improve your code further.
Lets start with the way you build strings. What you are currently using is slower when it comes to large strings. Instead, you should try using .join().
def encrypt(text, key): # Function that takes in two inputs: Text and key, which is the number of characters to shift forward
output, text = [], str(text)
limit = len(characters)
while key > limit:
key -= limit # This is my attempt to simplify the key
for i in text:
if i in characters:
output.append(characters[characters.index(i)+key]) # If i is in characters, the rotated character is concatenated to the output
else:
output.append(i) # Otherwise, it directly adds the text
return output.join()
Now your key optimization is basically just a modulo operation, so lets replace it with `key = key % len(characters).
def encrypt(text:str, key:int): # Function that takes in two inputs: Text and key, which is the number of characters to shift forward
output, text = [], str(text)
key = key % len(characters)
for i in text:
if i in characters:
output.append(characters[characters.index(i)+key]) # If i is in characters, the rotated character is concatenated to the output
else:
output.append(i) # Otherwise, it directly adds the text
return output.join()
Now comes your lookup method. It hopes for O(n) complexity, and has O(nm) complexity. Considering you might encrypt large texts, you may want to trade some space to save time. Lets use mapping structure instead.
And then add some finishing touches to make this prettier.
def encrypt(text:str, key:int, characters:str=characters):
'''
Function that shifts input by the number of buckets specified in key.
Unknown characters are retained.
:param text: text to be encrypted
:param key: number to shift by
:param characters: string specifying character ordering, None for default.
:return: encrypted string
'''
if not isinstance(text, str) or not isinstance(key, int):
raise ValueError('Incorrect param type')
output= []
l = len(characters)
key = key % l
char_map = { characters[i]:((i+key) % l) for i in characters}
for i in text:
ch = char_map.get(i)
if ch is None:
ch = i
output.append(ch)
return output.join()
output += characters[characters.index(i)+key]
output += characters[characters.index(i)-key]
The above lines are missing modulo operations.
characters.index(i)+key must be taken under modulo division with length of characters, which is limit in your code.
So, it should be,
output += characters[(characters.index(i)+key) % limit]
output += characters[(characters.index(i)-key) % limit]
This should work for you.
I'm trying to read a null terminated string but i'm having issues when unpacking a char and putting it together with a string.
This is the code:
def readString(f):
str = ''
while True:
char = readChar(f)
str = str.join(char)
if (hex(ord(char))) == '0x0':
break
return str
def readChar(f):
char = unpack('c',f.read(1))[0]
return char
Now this is giving me this error:
TypeError: sequence item 0: expected str instance, int found
I'm also trying the following:
char = unpack('c',f.read(1)).decode("ascii")
But it throws me:
AttributeError: 'tuple' object has no attribute 'decode'
I don't even know how to read the chars and add it to the string, Is there any proper way to do this?
Here's a version that (ab)uses __iter__'s lesser-known "sentinel" argument:
with open('file.txt', 'rb') as f:
val = ''.join(iter(lambda: f.read(1).decode('ascii'), '\x00'))
How about:
myString = myNullTerminatedString.split("\x00")[0]
For example:
myNullTerminatedString = "hello world\x00\x00\x00\x00\x00\x00"
myString = myNullTerminatedString.split("\x00")[0]
print(myString) # "hello world"
This works by splitting the string on the null character. Since the string should terminate at the first null character, we simply grab the first item in the list after splitting. split will return a list of one item if the delimiter doesn't exist, so it still works even if there's no null terminator at all.
It also will work with byte strings:
myByteString = b'hello world\x00'
myStr = myByteString.split(b'\x00')[0].decode('ascii') # "hello world" as normal string
If you're reading from a file, you can do a relatively larger read - estimate how much you'll need to read to find your null string. This is a lot faster than reading byte-by-byte. For example:
resultingStr = ''
while True:
buf = f.read(512)
resultingStr += buf
if len(buf)==0: break
if (b"\x00" in resultingStr):
extraBytes = resultingStr.index(b"\x00")
resultingStr = resultingStr.split(b"\x00")[0]
break
# now "resultingStr" contains the string
f.seek(0 - extraBytes,1) # seek backwards by the number of bytes, now the pointer will be on the null byte in the file
# or f.seek(1 - extraBytes,1) to skip the null byte in the file
(edit version 2, added extra way at the end)
Maybe there are some libraries out there that can help you with this, but as I don't know about them lets attack the problem at hand with what we know.
In python 2 bytes and string are basically the same thing, that change in python 3 where string is what in py2 is unicode and bytes is its own separate type, which mean that you don't need to define a read char if you are in py2 as no extra work is required, so I don't think you need that unpack function for this particular case, with that in mind lets define the new readString
def readString(myfile):
chars = []
while True:
c = myfile.read(1)
if c == chr(0):
return "".join(chars)
chars.append(c)
just like with your code I read a character one at the time but I instead save them in a list, the reason is that string are immutable so doing str+=char result in unnecessary copies; and when I find the null character return the join string. And chr is the inverse of ord, it will give you the character given its ascii value. This will exclude the null character, if its needed just move the appending...
Now lets test it with your sample file
for instance lets try to read "Sword_Wea_Dummy" from it
with open("sword.blendscn","rb") as archi:
#lets simulate that some prior processing was made by
#moving the pointer of the file
archi.seek(6)
string=readString(archi)
print "string repr:", repr(string)
print "string:", string
print ""
#and the rest of the file is there waiting to be processed
print "rest of the file: ", repr(archi.read())
and this is the output
string repr: 'Sword_Wea_Dummy'
string: Sword_Wea_Dummy
rest of the file: '\xcd\xcc\xcc=p=\x8a4:\xa66\xbfJ\x15\xc6=\x00\x00\x00\x00\xeaQ8?\x9e\x8d\x874$-i\xb3\x00\x00\x00\x00\x9b\xc6\xaa2K\x15\xc6=;\xa66?\x00\x00\x00\x00\xb8\x88\xbf#\x0e\xf3\xb1#ITuB\x00\x00\x80?\xcd\xcc\xcc=\x00\x00\x00\x00\xcd\xccL>'
other tests
>>> with open("sword.blendscn","rb") as archi:
print readString(archi)
print readString(archi)
print readString(archi)
sword
Sword_Wea_Dummy
ÍÌÌ=p=Š4:¦6¿JÆ=
>>> with open("sword.blendscn","rb") as archi:
print repr(readString(archi))
print repr(readString(archi))
print repr(readString(archi))
'sword'
'Sword_Wea_Dummy'
'\xcd\xcc\xcc=p=\x8a4:\xa66\xbfJ\x15\xc6='
>>>
Now that I think about it, you mention that the data portion is of fixed size, if that is true for all files and the structure on all of them is as follow
[unknow size data][know size data]
then that is a pattern we can exploit, we only need to know the size of the file and we can get both part smoothly as follow
import os
def getDataPair(filename,knowSize):
size = os.path.getsize(filename)
with open(filename, "rb") as archi:
unknown = archi.read(size-knowSize)
know = archi.read()
return unknown, know
and by knowing the size of the data portion, its use is simple (which I get by playing with the prior example)
>>> strins_data, data = getDataPair("sword.blendscn", 80)
>>> string_data, data = getDataPair("sword.blendscn", 80)
>>> string_data
'sword\x00Sword_Wea_Dummy\x00'
>>> data
'\xcd\xcc\xcc=p=\x8a4:\xa66\xbfJ\x15\xc6=\x00\x00\x00\x00\xeaQ8?\x9e\x8d\x874$-i\xb3\x00\x00\x00\x00\x9b\xc6\xaa2K\x15\xc6=;\xa66?\x00\x00\x00\x00\xb8\x88\xbf#\x0e\xf3\xb1#ITuB\x00\x00\x80?\xcd\xcc\xcc=\x00\x00\x00\x00\xcd\xccL>'
>>> string_data.split(chr(0))
['sword', 'Sword_Wea_Dummy', '']
>>>
Now to get each string a simple split will suffice and you can pass the rest of the file contained in data to the appropriated function to be processed
Doing file I/O one character at a time is horribly slow.
Instead use readline0, now on pypi: https://pypi.org/project/readline0/ . Or something like it.
In 3.x, there's a "newline" argument to open, but it doesn't appear to be as flexible as readline0.
Here is my implementation:
import struct
def read_null_str(f):
r_str = ""
while 1:
back_offset = f.tell()
try:
r_char = struct.unpack("c", f.read(1))[0].decode("utf8")
except:
f.seek(back_offset)
temp_char = struct.unpack("<H", f.read(2))[0]
r_char = chr(temp_char)
if ord(r_char) == 0:
return r_str
else:
r_str += r_char
Just trimmed this down big time
I have an overall assignment that must read a file, encrypt it and then write the encrypted data to a new file.
what i've tried is this:
filename=input("Enter file name:")
fr=open(filename)
keep_going=0
data = fr.readline()
fw=open('encrypted_file.txt', 'w')
for x in range(len(data)):
fw.write(data[x])
fw.close()
fr.close()
If your goal is just to exchange the letters in a string with others that you specify, then the solution is the following:
decrypted = 'abcdefghijklmnopqrstuvwxyz' #normal alphabet
encrypted = 'MNBVCXZLKJHGFDSAPOIUYTREWQ' #your "crypted" alphabet
#Encription
text = 'cryptme' #the string to be crypted
encrypted_text = ''
for letter in text:
encrypted_text += encrypted[decrypted.find(letter)]
print encrypted_text
#will print BOWAUFC
#Decription
text = encrypted_text #"BOWAUFC" in this example
decrypted_text = ''
for letter in text:
decrypted_text += decrypted[encrypted.find(letter)]
print decrypted_text
#will print cryptme
Note that your "crypted alphabet" do not convert any white space or any symbols but the lowercase letters, if you have other symbols in your text you have to include them as well.
However, this is not the proper way to encrypt anything! As suggested by others already, look up for a proper encryption algorithm.
I would suggest you look into Simple Crypt, this all depends on the level of security you want.
If I understand your question enough, Simple Crypt should do the job that you need.
https://pypi.python.org/pypi/simple-crypt
Here's a very simple implementation of the Vigenère Cipher I made:
from string import ascii_uppercase as alphabet
val = {}
for x in xrange(len(alphabet)):
val[alphabet[x]] = x
val[x] = alphabet[x]
encrypt = lambda a, b: ''.join(val[(val[a[i]]+val[b[i%len(b)]])%26] for i in xrange(len(a)))
decrypt = lambda a, b: ''.join(val[(val[a[i]]-val[b[i%len(b)]])%26] for i in xrange(len(a)))
Where a is the message and b is the key (I know it's written a bit tersely, but it was for a code golf competition). There are plenty of ciphers out there; you don't have to use this one, and probably shouldn't. It is just meant to get you thinking about possible ways to go about doing this. A very simple cipher that I think is good for your purposes is the Caesar Cipher.
One other thing that I'd like to point out is that your code doesn't look to modular -- one of your teacher's requirements -- right now. I'd recommend breaking it down to a function to open a file, a function to perform the actual **cryption, and a "main" function to take the user's input and call the other functions.
Best of luck to you!
I have been trying to write a simple encryption program in Python, and when I try to execute the code on Linux, it is not printing anything. Could someone please tell me why?
#!/usr/bin/env python2
import binascii
def encrypt():
text = raw_input("Please input your information to encrypt: ")
for i in text:
#Convert text into a binary sequence
i = bin(int(binascii.hexlify(i),16))
key = raw_input("Please input your key for decryption: ")
for j in key:
#Convert key into a binary sequence
j = bin(int(binascii.hexlify(j),16))
#This is only here for developmental purposes
print key
print text
Edit
I did what one of the users said, but my code still does not seem to be converting my plain text into binary like I want it too.
#!/usr/bin/env python2
def encrypt():
text = raw_input("Please input your information to encrypt: ")
for i in text:
#Convert text into a binary sequence
i = bin(ord(i))
key = raw_input("Please input your key for decryption: ")
for j in key:
#Convert key into a binary sequence
j = bin(ord(j))
#This is only here for developmental purposes
print key
print text
encrypt()
There are a number of major problems with your program:
You are defining a function called encrypt(), but never calling it.
Neither of your loops actually modifies the string. They modify loop variables, which are not the same thing!
int(binascii.hexlify(x), 16) is an overcomplicated way of writing ord(x).
bin() does not do what you are hoping for here.
Nothing is actually being XORed here. Your program is unfinished.
I think this is what you're trying to do
def encrypt():
cleartext = raw_input("Please input your information to encrypt: ")
ciphertext = []
for char in cleartext:
ciphertext.append(bin(ord(char))[2:])
ciphertext = ''.join(ciphertext)
key = raw_input("Please input your key for decryption: ")
decryptionKey = []
for char in key:
decryptionKey.append(bin(ord(char))[2:])
decryptionKey = ''.join(decryptionKey)
print "The key is '%s'" %decryptionKey
print "The encrypted text is '%s'" %ciphertext
I am trying to encrypt all possible strings in a defined character set then compare them to a hash given by user input.
This is what I currently have
import string
from itertools import product
import crypt
def decrypt():
hash1 = input("Please enter the hash: ")
salt = input("Please enter the salt: ")
charSet = string.ascii_letters + string.digits
for wordchars in product(charSet, repeat=2):
hash2 = crypt.METHOD_CRYPT((wordchars), (salt))
print (hash2)
Obviously its not finished yet but I am having trouble encrypting "wordchars"
Any help is appreciated
crypt.METHOD_CRYPT is not callable so the traceback that you provided doesn't correspond to the code in your question. crypt.METHOD_CRYPT could be used as the second parameter for crypt.crypt() function.
Also as #martineau pointed out wordchars is a tuple but you need a string to pass to the crypt.crypt() function.
From the docs:
Since a few crypt(3) extensions allow different values, with different
sizes in the salt, it is recommended to use the full crypted password
as salt when checking for a password.
To find a plain text from a defined character set given its crypted form: salt plus hash, you could:
from crypt import crypt
from itertools import product
from string import ascii_letters, digits
def decrypt(crypted, charset=ascii_letters + digits):
# find hash for all 4-char strings from the charset
# and compare with the given hash
for candidate in map(''.join, product(charset, repeat=4)):
if crypted == crypt(candidate, crypted):
return candidate
Example
salt, hashed = 'qb', '1Y.qWr.DHs6'
print(decrypt(salt + hashed))
# -> e2e4
assert crypt('e2e4', 'qb') == (salt + hashed)
The assert line makes sure that calling crypt with the word e2e4 and the salt qb produces qb1Y.qWr.DHs6 where qb is the salt.
Hmm may be better use bcrypt?
https://github.com/fwenzel/python-bcrypt
Below is a simple program that does what you asked:
def gen_word(charset, L):
if L == 1:
for char in charset:
yield char
raise StopIteration
for char in charset:
for word in gen_word(charset, L - 1):
yield char + word
def encrypt(word):
'''Your encrypt function, replace with what you wish'''
return word[::-1]
charset = ['1', '2', '3']
user_word = '12'
user_hash = encrypt(user_word)
max_length = 3
for length in range(1, max_length):
for word in gen_word(charset, length):
if encrypt(word) == user_hash:
print 'Word found: %s' % word
Basically, it uses a python generator for generating words from the charset of fixed length. You can replace the encrypt function with whatever you want (in the example is string reversal used as hash).
Note that with actual modern hashing methods, it'll take forever to decrypt an ordinary password, so I don't think you could actually use this.
Here's my completely different answer based on J.F. Sebastian's answer and comment about my previous answer. The most important point being that crypt.METHOD_CRYPT is not a callable even though the documentation somewhat confusingly calls a hashing method as if it were a method function of a module or an instance. It's not -- just think of it as an id or name of one of the various kinds of encryption supported by the crypt module.
So the problem with you code is two-fold: One is that you were trying to use wordchars as a string, when it actually a tuple produced by product() and second, that you're trying to call the id crypt.METHOD_CRYPT.
I'm at a bit of a disadvantage answering this because I'm not running Unix, don't have Python v3.3 installed, and don't completely understand what you're trying to accomplish with your code. Given all those caveats, I think something like the following which is derived from you code ought to at least run:
import string
from itertools import product
import crypt
def decrypt():
hash1 = input("Please enter the hash: ")
salt = input("Please enter the salt: ")
charSet = string.ascii_letters + string.digits
for wordchars in product(charSet, repeat=2):
hash2 = crypt.crypt(''.join(wordchars), salt=salt) # or salt=crypt.METHOD_CRYPT
print(hash2)