Question

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¥)ÊãjnÞª»^}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ヒÓ!Â`゚
Was it helpful?

Solution

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top