Decrypt AES encrypted file, function causing failure
I am trying to write a C ++ program to encrypt and decrypt certain files. The encryption works fine, but when I come to decrypt the function it seems to be causing the program to crash.
Here is the function I am using to decrypt:
CString DecryptFile(CString csSourceFile)
{
CString csDecryptedFile = csSourceFile;
CRijndael aesEncryption;
int nPos = csDecryptedFile.ReverseFind('.');
if(nPos == -1)
{
//Log("ERROR:: file name not proper");
return CString("");
}
csDecryptedFile = csDecryptedFile.Left(nPos);
csDecryptedFile += ".wav";
FILE *fIn = fopen(csSourceFile.GetBuffer(0),"rb");
FILE *fOut = fopen(csDecryptedFile.GetBuffer(0),"wb");
if(!fIn || !fOut)
{
//Log("ERROR:: failed to Open File for encryption");
return CString("");
}
int nlen = -1;
aesEncryption.MakeKey(AM_ENC_KEY,AM_NULL_KEY,BLOCK_SIZE,BLOCK_SIZE);
int nRead = 0;
while (true) {
char szBlockIn[EBLOCK_SIZE+1] = {0};
char szBlockOut[BLOCK_SIZE+1] = {0};
char szBlockDec[BLOCK_SIZE+1] = {0};
memset(szBlockIn,0,sizeof(char)*(EBLOCK_SIZE+1));
memset(szBlockOut,0,sizeof(char)*(BLOCK_SIZE+1));
memset(szBlockDec,0,sizeof(char)*(BLOCK_SIZE+1));
int nRead = fread(szBlockIn,sizeof(char),EBLOCK_SIZE, fIn);
if(nRead <= 0) {
break;
}
nlen = EBLOCK_SIZE;
Decode(szBlockIn,szBlockOut,nlen);
aesEncryption.DecryptBlock((char *)szBlockOut,szBlockDec);
fwrite(szBlockDec,sizeof(char),BLOCK_SIZE,fOut);
}
fclose(fIn);
fclose(fOut);
RemoveEndTag(csDecryptedFile.GetBuffer(0));
AfxMessageBox(csDecryptedFile);
AfxMessageBox(_T("returning"));
return csDecryptedFile;
}
This is the piece of code that calls the function:
CString strTest = DecryptFile(m_DecompressedTempFile);
AfxMessageBox(strTest);
The strange thing is that decryption works fine - if I get into the folder with the file, I can see the file with the decryption and access it. Unfortunately, the program has to do more than that, and the program just hangs right after. Particularly weird is that as you can see I am calling AfxMessageBox(_T("returning"));
to the end of the function: this creates a message ok. But when I call AfxMessageBox(strTest);
from the code that runs this function, no message box is created.
Through debugging, it seems that the problem is somehow routed in the while loop I have to encrypt the file, so I'm wondering if I'm closing something that I should or something? Not really sure what to do with this, so anyone who can offer any help would be great!?!
Thanks in advance.
I forgot to mention that this has been working for many years - the only recent change is that we are now compiling the code on Windows 7, while it used to be Windows XP. Will it matter to anything in the function?
Update: It's worth noting that if I remove the while loop and run the contents of the loop only once, the same problem occurs. If I delete aesEncryption.DecryptBlock((char *)szBlockOut,szBlockDec);
then the problem goes away, but obviously I need this line. So the problem is directed towards this feature I guess. Although this feature exists inside the library, I have included it below:
//Decrypt exactly one block of ciphertext.
// in - The ciphertext.
// result - The plaintext generated from a ciphertext using the session key.
void CRijndael::DecryptBlock(char const* in, char* result)
{
if(false==m_bKeyInit)
throw exception(sm_szErrorMsg1);
if(DEFAULT_BLOCK_SIZE == m_blockSize)
{
DefDecryptBlock(in, result);
return;
}
int BC = m_blockSize / 4;
int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
int s1 = sm_shifts[SC][1][1];
int s2 = sm_shifts[SC][2][1];
int s3 = sm_shifts[SC][3][1];
//Temporary Work Arrays
int i;
int tt;
int* pi = t;
for(i=0; i<BC; i++)
{
*pi = ((unsigned char)*(in++) << 24);
*pi |= ((unsigned char)*(in++) << 16);
*pi |= ((unsigned char)*(in++) << 8);
(*(pi++) |= (unsigned char)*(in++)) ^= m_Kd[0][i];
}
//Apply Round Transforms
for(int r=1; r<m_iROUNDS; r++)
{
for(i=0; i<BC; i++)
a[i] = (sm_T5[(t[i] >> 24) & 0xFF] ^
sm_T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
sm_T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
sm_T8[ t[(i + s3) % BC] & 0xFF]) ^ m_Kd[r][i];
memcpy(t, a, 4*BC);
}
int j;
//Last Round is Special
for(i=0,j=0; i<BC; i++)
{
tt = m_Kd[m_iROUNDS][i];
result[j++] = sm_Si[(t[i] >> 24) & 0xFF] ^ (tt >> 24);
result[j++] = sm_Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16);
result[j++] = sm_Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8);
result[j++] = sm_Si[ t[(i + s3) % BC] & 0xFF] ^ tt;
}
}
source to share
it's not clear if you only want a C ++ solution; if you are ok with a Python solution try the PyCrypto module. Here is what I use to zip the current directory, except for some ignoring templates and .zip's encryption. / bin (most of the code is copied from other sources on SO):
import sys
import os
import zipfile
import getpass
from contextlib import contextmanager
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len('Salted__'))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write('Salted__' + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
def query_yes_no(question, default="yes", quiet=False):
"""Ask a yes/no question via raw_input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is True for "yes" or False for "no".
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
if not quiet:
sys.stdout.write(question + prompt)
if quiet and default is not None:
choice=default
else:
choice = raw_input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n')\n")
dircurr=os.path.abspath('.').replace('\\','/')
dirwithenc=dircurr+'/bin'
if os.path.isfile(dirwithenc+'/bak.enc'):
if query_yes_no("\nDo you want to decrypt '"+dirwithenc+"/bak.enc'?",'no'):
password=getpass.getpass('\nDecryption password: ')
with open(dirwithenc+'/bak.enc', 'rb') as in_file, open(dircurr+'/bak.zip', 'wb') as out_file:
decrypt(in_file,out_file,password)
print("\nFile '"+dircurr+"/bak.enc' decrypted to '"+dircurr+"/bak.zip'")
raw_input("""
***DONE***
Press Enter...""")
exit(0)
else:
print("\nAnswered 'no'")
print("\nEncrypting files will overwrite any preexisting file '"+dirwithenc+"/bak.enc'")
password=getpass.getpass('\nSet an encryption password (will not be saved): ')
print('\nZipping files, excepting certain ignore patterns...')
zf = zipfile.ZipFile(dircurr+'/bak.zip', 'w')
for dirname, subdirs, files in os.walk(os.path.abspath('.')):
#Do not archive system files beginning with period
for filename in files:
if filename[:1]=='.':
files.remove(filename)
#Do not archive the binaries directory, it should just be delivered as is
if 'bin' in subdirs:
subdirs.remove('bin')
#Do not archive any previous archives, or you will just make a giant snowball
if 'bak.zip' in files:
files.remove('bak.zip')
if 'bak.enc' in files:
files.remove('bak.enc')
zf.write(dirname)
for filename in files:
zf.write(os.path.join(dirname, filename))
#If there was a folder 'dat' under the binaries folder, then archive it too,
#so that you have everything you need to rebuild the project
print("Including files under 'bin/dat'")
if os.path.isdir(dircurr+'/bin/dat'):
for dirname, subdirs, files in os.walk(dircurr+'/bin/dat'):
if 'bin' in subdirs:
subdirs.remove('bin')
zf.write(dirname)
for filename in files:
zf.write(os.path.join(dirname, filename))
#If there were any files '*.ico' under the binaries folder, then archive them too,
#so that you have everything you need to rebuild the project
print("Including .ico files under 'bin'")
for dirname, subdirs, files in os.walk(dircurr+'/bin'):
for filename in files:
if filename[-4:]=='.ico':
zf.write(os.path.join(dirname, filename))
zf.close()
print("\nZipped to '"+dircurr+"/bak.zip'")
print("\nEncrypting zipped file and removing unencrypted zipped file...")
with open(dircurr+'/bak.zip', 'rb') as in_file, open(dirwithenc+'/bak.enc', 'wb') as out_file:
encrypt(in_file, out_file, password)
os.remove(dircurr+'/bak.zip')
print("\nEncrypted to '"+dirwithenc+"/bak.enc'")
raw_input("""
***DONE***
Press Enter...""")
exit(0)
source to share