I am currently having an issue of not being able to decrypt the text provided after encryption. It returns:
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unpadded Text: Hello World
Padded Text: Hello World
Salt: h5eE0b814M
Encrypted Text: WxCž~¼!Ò]Cú´=P+
Encrypted Text with Salt: h5eE0b814MWxCž~¼!Ò]Cú´=P+
Key: ンƚ!*!゙ᆱø}Qd`Dᆱd!Þxͦ}ᄚミᄀ>'U
Unencrypted Text:
Where
Unencrypted Text:
Should be "Unencypted Text: Hello World"
Two programs are used in this, one a module and a master. You must run the master to run the module. Any adivce or help would be greatly appricated as I have been stuck for a while. Thank you for your time.
Here is the code:
Master.py
import Encryption as encrypt
#Place Holder Variables
SALT_SIZE = 16
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
#Adjustable Variables
text = "Hello World"
iterations = 62705
salt = 'h5eE0b814M'
password = 'pause232'
encrypt.key_generation(password, salt, iterations)
encrypt.encryption(text, password, SALT_SIZE, salt, iterations)
encrypt.decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
Encryption.py
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
#Key Generation(Used in encyption to create cipher)
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
print '\nKey: ' + key #Debug Print
return key
#Text padding function to set text to a incerment of SALT_SIZE
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE #Using the length of [text] it counts how many more characters is required to make an incerment of [SALT_SIZE]
pad_size = SALT_SIZE - extra_bytes #Subtracts the needed bytes from the [SALT_SIZE] and sets [pad_size] as the length of pading needed.
pad = chr(pad_size) * pad_size #Creates padding for the [text]
padded_text = text + pad #Adds the [pad] to the [text]
print '\nPadded Text: ' + padded_text #Debug Print
#Primary Encryption Function(using text and password)
def encryption(text, password, SALT_SIZE, salt, iterations):
global padded_text
global ciphertext
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
#Primary Decryption Function(using the encrypted text and password)
def decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations):
ciphertext_with_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
unencrypted_text = cipher.decrypt(ciphertext_with_salt)
print '\nUnencrypted Text: ' + unencrypted_text #Debug Print
return unencrypted_text
#Code to allow to use as outside module
if __name__ == '__main__':
key_generation(password, salt, iterations)
encryption(text, password, SALT_SIZE, salt, iterations)
decryption(ciphertext_with_salt, password, SALT_SIZE, salt, iterations)
You are transferring your ciphertext and key as strings. You should make sure that the bytes that make up the ciphertext and keys stay intact. If you want to transport them using character strings, please use either hexadecimal encoding or base 64. Hexadecimals are easier to read and check for length, base 64 is more efficient.
Note that the ciphertext will contain bytes with any value, including ones that are ASCII control characters (< 0x20) which also contain termination characters such as 0x00.
Related
I need to sent my friend Bob the number 42, my code will use a pre-shared key generate a random number to encrypt the message (42) and decrypt it on Bobs end. the only problem is, I have no clue how to generate a repeatable encryption key in python.
You can use Crypto to encrypt a message using AES.
import hashlib, base64
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher():
def __init__(self, key):
self.private_key = hashlib.sha256(key.encode()).digest()
self.bs = AES.block_size
def encrypt(self, data):
# generate public key
public_key = Random.new().read(self.bs)
# setup AES Cipher using public key and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# enrpyt the data and convert to base64
return base64.b64encode(public_key + cipher.encrypt(self.pad(data).encode()))
def decrypt(self, enc):
# convert encrypted data to base 64
enc = base64.b64decode(enc)
# get public key
public_key = enc[:AES.block_size]
# setup AES Cipher using public and private key
cipher = AES.new(self.private_key, AES.MODE_CBC, public_key)
# decrypt data using the public key
return self.unpad(cipher.decrypt(enc[AES.block_size:])).decode("utf-8")
def pad(self, s):
# pads data so that it's a multiple of 16
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
def unpad(self, s):
# removes padding
return s[:-ord(s[len(s)-1:])]
cipher = AESCipher("your secret key")
Here I encrypt some text, the public key with the encrypted text is joined and returned from the encrypt method, you can then send the returned text to Bob, to decrypt the text bob then just needs to run the decrypt method.
>>> cipher.encrypt("your message")
b'HYfUkcd//CaRquG9AhReR8bJYdVQdcGWRAjcp9AstLs='
>>> output = cipher.encrypt("your message")
>>> output
b'RVTK7L7ZDw9DzvuXuj8zYPZJjBO/A0N3l5N1hp9LY6U='
>>> cipher.decrypt(output)
'your message'
>>>
I have been working on a purely PROOF OF CONCEPT code stemming from the Diffie Hellman keypair exchange through SHA-256 encryption of those keys to then using that as a key for AES-256 encryption of text. I am only writing it for personal use. I know the prime numbers and secrets would need to be much longer..
The only code I have an issue with is the AES-256 encryption. It appears to work as it shows the encrypted text and is able to decrypt it but upon closer inspection, the encrypted text changes every time you run the program? I find this strange as the program is able to encrypt the given plain text, generate encrypted text and then decrypt this using the key given. However, this encrypted text changes every time the program is run using the same plaintext and same key.
Please see below the code...
from __future__ import print_function
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
from base64 import b64encode, b64decode
# Variables
sharedPrime = 3721728827 # p
sharedBase = 2097383831 # g
p1Secret = 927391 # a
p2Secret = 193749 # b
# Begin
print( "\n--------------------------------------------\n" )
print( "Publicly Shared Variables:")
print( " Publicly Shared Prime (p): " , sharedPrime )
print( " Publicly Shared Base (g): " , sharedBase )
print( "\nPrivate Variables:")
print( " Private key a (a): " , p1Secret )
print( " Private key b (b): " , p2Secret )
print( "\n--------------------------------------------\n" )
# Person1 Sends Person2 A = g^a mod p
A = (sharedBase**p1Secret) % sharedPrime
print("Public Keys Sent Over Public Chanel: ")
print(" A = g^b mod p")
print(" Person1 Sends Over Public Chanel: " , A )
# Person2 Sends Person1 B = g^b mod p
B = (sharedBase ** p2Secret) % sharedPrime
print(" B = g^a mod p")
print(" Person2 Sends Over Public Chanel: ", B )
print( "\n--------------------------------------------\n" )
print( "Privately Calculated Shared Secret: " )
# P1 Computes Shared Secret: s = B^a mod p
p1SharedSecret = (B ** p1Secret) % sharedPrime
print( " s = B^a mod p")
print( " Person1 Shared Secret: ", p1SharedSecret )
# P2 Computes Shared Secret: s = A^b mod p
p2SharedSecret = (A**p2Secret) % sharedPrime
print( " s = A^b mod p")
print( " Person2 Shared Secret: ", p2SharedSecret )
print( "\n--------------------------------------------\n" )
# Converts DH secret to SHA256
print( "Converting shared key to SHA-256 key")
secretbyte = str(p1SharedSecret).encode()
print( " Shared secret to bytes: ", secretbyte)
shaV = hashlib.sha256(secretbyte).hexdigest()
print( " SHA-256 key: ", shaV)
key32 = shaV[0:32]
print( " SHA-256 key to AES-256 32bit secret key: ", key32)
print( "\n--------------------------------------------\n" )
#Begin AES-256 encryption
print( "Using SHA-256 32bit key for AES-256 encryption")
class AESCipher(object):
def __init__(self, key):
self.block_size = AES.block_size
self.key = hashlib.sha256(secretbyte).digest()
def encrypt(self, plain_text):
plain_text = self.__pad(plain_text)
iv = Random.new().read(self.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
encrypted_text = cipher.encrypt(plain_text.encode())
return b64encode(iv + encrypted_text).decode("utf-8")
def decrypt(self, encrypted_text):
encrypted_text = b64decode(encrypted_text)
iv = encrypted_text[:self.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
plain_text = cipher.decrypt(encrypted_text[self.block_size:]).decode("utf-8")
return self.__unpad(plain_text)
def __pad(self, plain_text):
number_of_bytes_to_pad = self.block_size - len(plain_text) % self.block_size
ascii_string = chr(number_of_bytes_to_pad)
padding_str = number_of_bytes_to_pad * ascii_string
padded_plain_text = plain_text + padding_str
return padded_plain_text
#staticmethod
def __unpad(plain_text):
last_character = plain_text[len(plain_text) - 1:]
return plain_text[:-ord(last_character)]
#Accept user input for encryption
message = input("Please enter message you want to be encrypted: ")
encMessage = AESCipher(key32)
encrypted = encMessage.encrypt(message)
print("Your encrypted message is: \n", encrypted)
print("Person 2 will now decrypt this message using the shared public key")
decrypted = encMessage.decrypt(encrypted)
print("Your decrypted message is: \n", decrypted)
The code produces the SHA-256 key of 72538667a2065257993b531746b9d92527cfe3caecc1457c4842e6a6caffe472 which I then truncate down to 32 characters to produce the AES-256 key of 72538667a2065257993b531746b9d925. Any plaintext I then enter will be encoded and decoded successfully, however the encrypted text changes each time even though the same key and plaintext are entered. Am I doing something wrong?
As long as your ciphertext can be decrypted successfully every time, you don't need to worry about the ciphertext being different.
Your code uses CBC chaining mode, which is common that a different Initialization Vector is generated each time you encrypt something, to avoid identical plaintext looking identical after encryption (which would partially defeat the point of encryption since an attacker now knows whether it's the same plaintext).
Image source: Block cipher mode of operation - Wikipedia (public domain)
I'm trying to create encrypt & decrypt functions using pyDES module.
Problem:
if I print the cipher text on the screen then copy it (CTRL+C) then put it as input for the decrypt() function I get the following exception:
ValueError: Invalid data length, data must be a multiple of 8 bytes
my code:
key = "12345678"
charset = "utf-8"
def encrypt():
msg = input("Enter message: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
cipherText = data.encrypt(msg.encode(charset))
print(cipherText)
def decrypt():
cipherText = input("Enter cipher text: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
decrypted = data.decrypt(cipherText).decode(charset)
print(decrypted)
While passing the cipherText directly to decrypt message (not by copying it manually from the screen) it works perfectly! like this:
key = "12345678"
charset = "utf-8"
msg = input("Enter message: ")
data = des(key, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
cipherText = data.encrypt(msg.encode(charset))
print(cipherText)
decrypted = data.decrypt(cipherText).decode(charset)
print(decrypted)
fix/explanation would be much appreciated.
I am running into the issue of my decrypt_file function unable to decrypt text which was encrypted, No errors are provided just unexpected returns.
The encryption function uses salt and a password to encrypt a text file, then saves it as a .enc file, Then after attempting to run my decrypt_file function, it identifies the salt, iv, password, iterations, ect but does not decrypt() properly. Here is the code. And a dry run. Any guidance or help would be appreciated.
Encryption/Decryption:
from Crypto import Random
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom
import hashlib
def key_generation(password, salt, iterations):
assert iterations > 0
print "Salt: " + salt, '\n', "Password: " + password
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
print '\nKey: ' + key #Debug Print
return key
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encryption(message, password, salt, iterations, key_size=256):
key = key_generation(password, salt, iterations)
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
print "Random IV: " + iv
enc = salt + iv + cipher.encrypt(message)
print "Length: " + str(len(enc))
enc = pad(enc)
print "Length: " + str(len(enc))
return enc
def decryption(ciphertext, password, iterations):
salt = ciphertext[0:16]
iv = ciphertext[:AES.block_size]
print len(str(iv))
print len(str(salt))
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[AES.block_size:])
print "Plaintext: " + plaintext
return plaintext.rstrip(b"\0")
def encrypt_file(file_name, password, salt, iterations):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
print "Text: " + plaintext
enc = encryption(plaintext, password, salt, iterations)
print "Encrypted Contents: " + enc
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_file(file_name, password, iterations):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
print "Cipher'd Text: " + ciphertext
dec = decryption(ciphertext, password, iterations)
with open(file_name[:-4], 'wb') as fo:
fo.write(dec)
encrypt_file('HelloWorld.txt', 'password', 'randomsalt', 64000)
decrypt_file('HelloWorld.txt.enc', 'password', 64000)
Dry Run:
Encrypt:
Text: }¦—Z“Íd¥çgZw?øÈH™«Nœfra¥)ÊãjnÞª»^}K^Ì„¦ý
×¾Šv“_3w€mG9‚ä¤Å¥žUƯ0£Óy®0²
nrfÖÖ «–‰¯ò
Salt: randomsalt
Password: password
Key: /Îbdヘ5è!ニヒAᆰv=L*øK/ò)Ü
Random IV: eミý1ËÈUÓbIワᄡムl
Length: 138
Length: 144
Encrypted Contents: randomsalteミý1ËÈUÓbIワᄡムl$֭>oリ)<L゙y\I!%wÙßÞlモÊJt$ワ
è Ì-ᄈMᄀ8ヘ!ᄚܩᄃÆ4ÒO9AÃðO.ä3}ヘål{ヌヒ#ÿËzᄋgDᆰ\éUʼaà8タLᅠMupaÔAミマX0ンᄉi3ヨ˧cᄃ;ᆱÛo
Decrypt:
Cipher'd Text: randomsalteミý1ËÈUÓbIワᄡムl$֭>oリ)<L゙y\I!%wÙßÞlモÊJt$ワ
è Ì-ᄈMᄀ8ヘ!ᄚܩᄃÆ4ÒO9AÃðO.ä3}ヘål{ヌヒ#ÿËzᄋgDᆰ\éUʼaà8タLᅠMupaÔAミマX0ンᄉi3ヨ˧cᄃ;ᆱÛo
16
16
Salt: randomsalteミý1Ë
Password: password
Key: 1ÜA !TzxGÑ`wß~|º‹|¡(—ª-%òÇŒÖ
Plaintext: Rネ*SᄊÕñÛ.
t-Îテýト͛'úᄎSタ&2ᆴæマéヌᄏýýᄑtçØÉe?×ûìrモᄈÞcᄎᄇ×_Kメ
ᄎÀ~ä'ᄅ,ᄉ-Èᄀt&gSð:WÕ|
メ^リøᄃ]ノヤÏYvísgl/ᆵレホ*`\ᄚåᄌŴÇlヒÓ!Â`゚
So please print things out in hexadecimals. You can then clearly see that indeed during decrypt that the salt and IV were overlapping. In your code you were also assuming a different length salt than that was given.
from Crypto import Random
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from os import urandom
import hashlib
import binascii
def key_generation(password, salt, iterations):
assert iterations > 0
print "Salt: " + tohex(salt), '\n', "Password: " + password
key = password + salt #Combines [password] and [salt] to create a [key]
for i in range(iterations): #Hashes the [key]
key = hashlib.sha256(key).digest() #Using Sha256 it hashes the [key] based on amount of [iterations]
return key
def pad(s):
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def encryption(message, password, salt, iterations, key_size=256):
print " === ENCRYPTION === "
key = key_generation(password, salt, iterations)
print "Key: " + tohex(key)
message = pad(message)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
print "Random IV: " + tohex(iv)
enc = salt + iv + cipher.encrypt(message)
return enc
def decryption(ciphertext, password, iterations):
print " === DECRYPTION === "
salt = ciphertext[0:10]
iv = ciphertext[10:10+AES.block_size]
print "Random IV: " + tohex(iv)
key = key_generation(password, salt, iterations)
print "Key: " + tohex(key)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext[10+AES.block_size:])
print "Plaintext: " + plaintext
return plaintext.rstrip(b"\0")
def encrypt_file(file_name, password, salt, iterations):
with open(file_name, 'rb') as fo:
plaintext = fo.read()
print "Text: " + plaintext
enc = encryption(plaintext, password, salt, iterations)
print "Encrypted Contents: " + enc
with open(file_name + ".enc", 'wb') as fo:
fo.write(enc)
def decrypt_file(file_name, password, iterations):
with open(file_name, 'rb') as fo:
ciphertext = fo.read()
print "Cipher'd Text: " + ciphertext
dec = decryption(ciphertext, password, iterations)
with open(file_name + ".dec", 'wb') as fo:
fo.write(dec)
def tohex(data):
return binascii.hexlify(data)
encrypt_file('HelloWorld.txt', 'password', 'randomsalt', 64000)
decrypt_file('HelloWorld.txt.enc', 'password', 64000)
I am struggling with converting this piece of code into a module in where I can use an outside program to set the variables in this piece of code. How would I go about setting variables in one program and set them in another(to be used as a module) and get the results from said program into the first program.
Here is the code, any advice/help would be greatly appreciated.
import Crypto.Random
from Crypto.Cipher import AES
import hashlib
SALT_SIZE = 16
iterations = 64000
salt = 'h5eE0b814M'
password = 'fortytwo'
text = 'What do you mean'
padded_text = ''
ciphertext = ''
key = ''
ciphertext_with_salt = ''
def key_generation(password, salt, iterations):
global key
assert iterations > 0
key = password + salt
for i in range(iterations):
key = hashlib.sha256(key).digest()
print '\nKey: ' + key #Debug Print
return key
def pad_text(text, SALT_SIZE):
print '\nUnpadded Text: ' + text #Debug Print
global padded_text
extra_bytes = len(text) % SALT_SIZE
pad_size = SALT_SIZE - extra_bytes
pad = chr(pad_size) * pad_size
padded_text = text + pad
print '\nPadded Text: ' + padded_text #Debug Print
return padded_text
def encryption(text, password):
global ciphertext
global key
salt1 = Crypto.Random.get_random_bytes(SALT_SIZE)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = pad_text(text, SALT_SIZE)
ciphertext = cipher.encrypt(padded_text)
ciphertext_with_salt = salt + ciphertext
#debug script
print '\nSalt: ' + salt #Debug Print
print '\nEncrypted Text: ' + ciphertext #Debug Print
print '\nEncrypted Text with Salt: ' + ciphertext_with_salt #Debug Print
return ciphertext_with_salt
def decryption(ciphertext, password):
salt = ciphertext[0:SALT_SIZE]
ciphertext_sans_salt = ciphertext[SALT_SIZE:]
key = key_generation(password, salt, iterations)
cipher = AES.new(key, AES.MODE_ECB)
padded_plaintext = cipher.decrypt(ciphertext_sans_salt)
print '\nUnencrypted Text: ' + text #Debug Print
return text
key_generation(password, salt, iterations)
encryption(text, password)
decryption(ciphertext, password)
Simply put
if __name__ == '__main__':
Before your last 3 lines. Then you can import it like any other module and call functions with e.g. yourmodulename.encryption(text, password).