views:

92

answers:

1

Hello, I am trying to run a program, that encrypts and decrypts using AES.

(from http://www.codeproject.com/KB/security/AESProductKey.aspx )

// From aestest1.cpp

// Runtime Includes
#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <string>
#include "stdafx.h"

// Crypto++ Includes
#include "cryptlib.h"

#include "aes.h"        // AES

#include "modes.h"      // CBC_Mode< >

#include "filters.h"    // StringSource

using namespace std;

int main(int argc, char* argv[]) {

// Key and IV setup
byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], 
      iv[ CryptoPP::AES::BLOCKSIZE ];

::memset( key, 0x01, CryptoPP::AES::DEFAULT_KEYLENGTH );
::memset(  iv, 0x01, CryptoPP::AES::BLOCKSIZE );

// Message M
string PlainText = "Hello AES World";

// Debug
cout << "Plain Text:" << endl;
cout << "  '" << PlainText << "'" << endl;
cout << endl;

// Cipher Text Sink
string CipherText;

// Encryption
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption
    Encryptor( key, sizeof(key), iv );

CryptoPP::StringSource( PlainText, true,
    new CryptoPP::StreamTransformationFilter( Encryptor,
        new CryptoPP::StringSink( CipherText )
    ) // StreamTransformationFilter
); // StringSource


///////////////////////////////////////
//                DMZ                //
///////////////////////////////////////
//Write data
ofstream write ("file.txt", ios::out | ios::binary);
write.write((char*)key,sizeof(key));
write.write((char*)iv,sizeof(iv));
int at = CipherText.length();
write.write(CipherText.c_str(),at); 
write.close();
CipherText.erase();
//Using new key and iv later;
byte key1[ CryptoPP::AES::DEFAULT_KEYLENGTH ], 
     iv1[ CryptoPP::AES::BLOCKSIZE ];

//Read data
ifstream read ("file.txt", ios::in | ios::binary);

read.seekg (0, ios::end);
int fsize = read.tellg();
read.seekg (0, ios::beg);

read.read((char*)key1,sizeof(key));
read.read((char*)iv1,sizeof(iv));




int toRead = fsize - sizeof(key) - sizeof(iv);
vector<char> bData(toRead);
read.read(&bData[0],toRead);

read.close();
// Recovered Text Sink
string RecoveredText;

// Decryption
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption
    Decryptor( key1, sizeof(key1), iv1 );

CryptoPP::StringSource( &bData[0], true,
    new CryptoPP::StreamTransformationFilter( Decryptor,
        new CryptoPP::StringSink( RecoveredText )
    ) // StreamTransformationFilter
); // StringSink

// Debug
cout << "Recovered Text:" << endl;
cout << "  '" << RecoveredText << "'" << endl;
cout << endl;
system("pause");
return 0;

} So I couldn`t manage to write a code, that will correctly do the stuff, that mentioned in comments (after DMZ, where ofstream begins). Thanks in advance.

+2  A: 

You need to disable text processing, which messes up newline characters.

Try

ofstream write ("text.txt", ios::out | ios__binary);

and

ifstream read ("text.txt", ios::in | ios::binary);
Ben Voigt
Thank you very much!
EdgeLuxe
Was that the only problem you were having?
Ben Voigt
Oh, I was satisfied with writing the CipherText only binary mode worked for it perfectly. Now I seem to have problems writing and reading key[] and iv[]. Maybe writing and reading using the for loop and DEFAULT_KEYLENGTH/BLOCKSIZE will help? I can`t write that code myself. Thanks in advance.
EdgeLuxe
I would suggest writing `key` and `iv` before the ciphertext, because they have fixed length. You can use for example `ofstream write; write.write(iv, sizeof iv / sizeof *iv);` and the matching function `read` to transfer exactly the right number of characters. Then there's no difficulty figured out where the ciphertext starts and ends.
Ben Voigt
Thanks, could you tell me what is wrong in edited code? The Read/Write part, of course. (using namespace std; is used in this part of code, I have just merged the two files for this question, so don't look that std:: is missing)
EdgeLuxe
Looks good for `key` and `iv`, however you can't treat a `std::string` the same as a `byte[]`. Especially `sizeof (CipherText)` is telling you nothing (it's the size of the string metadata, not the size of the content), try `CipherText.length()` instead.
Ben Voigt
Replaced the line with `int at = CipherText.length();`Program seems to be crashing, as it was. What useful info I can paste here for you?
EdgeLuxe
You're changing a `std::string` by writing to the pointer returned by `c_str()`. That's not allowed. Read into an array or `std::vector` or something you can create a string from (or pass into CryptoPP directly). Also, you didn't originally say it was crashing, just that something was wrong, so I thought you might be getting the wrong results but no error. If you run it in the debugger, in what line of code does the program crash?
Ben Voigt
It throws exception: `StreamTransformationFilter: ciphertext length is not a multiple of block size`At: `CryptoPP::StringSource( CipherText, true, new CryptoPP::StreamTransformationFilter(Decryptor, new CryptoPP::StringSink( RecoveredText )));`So that is because something is wrong with CipherText string. I would like to use a string, because I use its functions quite often later. So, I could (I guess) convert it to a `const char*` using the `stringstream`, but the problem is it will use more RAM. EDIT: got it. Read to a vector and put results to a string. I will try it.
EdgeLuxe
Hmm, assuming I want to use as less memory as I could, I would like to read to `const char*` or `const std::string` or `const byte` (giving CryptoPP the length), because I can pass them to CryptoPP directly. Could you provide me the sample?
EdgeLuxe
Ben Voigt
Seems to be quite complicated for me. Maybe I should read to a string like usual (`read >> CipherText`) and pass `key` and `iv` as an argument when launching the executable? Is it possible or not? If not, uh, well, I don`t get it with vectors.
EdgeLuxe
If you use the extraction operator like that, you'll stop reading when you hit the first whitespace (such as space, tab, newline). `getline` is a little better (there's only one character that will terminate the read, but that's still no good for binary data. The `read` and `write` functions are the only ones that can be used with binary data. Note that since even the 'NUL' character can exist inside the ciphertext block, it's not good to treat ciphertext as a string.
Ben Voigt
Yes, I have just didn`t want to write the whole code for reading a file. I have thought that `read`/`write` would be better, bet I still haven`t figured out reading, yet. So, let say the array of bytes `key` has 16 members. `key` will be accessable, let`s say, at `argv[3]`, or every member will be accessable through `argv[3]` and `argv[19]`?
EdgeLuxe
When did command-line arguments come in? You can't pass binary data on the command-line, instead try a hexadecimal representation (whether you use spaces as a separator will determine whether the hex digits representing `iv` will all be in `argv[3]` or `argv[3]` ... `argv[18]`). Then you have to parse hexadecimal numbers, for example `sscanf` can help with this. I don't see how you're saving any complexity by using the command-line.
Ben Voigt
Well, I just couldn`t deal with vectors yesterday, so I thought that command line arguments will help me with passing `key` and `iv` to executable, instead of creating text file. Anyway, I have edited the code in question, it seems to be working fine. Could you please look whether there are some bugs? Thank you very much.
EdgeLuxe
A slightly roundabout way of finding out the file size, but everything looks right. You've calculated the ciphertext size correctly (although you might want to use either `sizeof(iv)` or `CryptoPP::AES::BLOCKSIZE` and not a mixture of the two). You've preallocated the `std::vector` correctly. Everything looks bug free. Even better: `int toRead = fsize - read.tellg();`, which won't break if you decide to change the header format.
Ben Voigt
Thank you very much for your help! Guess I bothered you quite much, but still, thank you very much!
EdgeLuxe
I wouldn't be on SO if answering questions bothered me.
Ben Voigt
`char * key_data = new char [keyLength];``char * iv_data = new char [ivLength];``read_answer.read(key_data,keyLength);``read_answer.read(iv_data,keyLength);``somefunc(iv_data);``somefunc(key_data);``read_answer` is `ofstream` that opens `file.txt` in binary. `file.txt` contains key,iv,CipherText. It was generated by a program in the question. `somefunc` accepts `char *` to be passed in. `keyLength` and `ivLength` are equal to 16. The problem is, that program reads 16 correct bytes, and adds some 6 bytes. Same with `key_data` and `iv_data`, so they become 22 bytes length.
EdgeLuxe
EdgeLuxe
You can't use `sizeof` with dynamically allocated memory (such as `new char[]`), `sizeof` is calculated at compile time.
Ben Voigt
Hmm.. When handling big text (sometimes more than 50 bytes, sometimes more that 300) program throws:`StreamTransformationFilter: ciphertext length is not a multiple of block size` and crashes (on decryption as I found out). Any ideas?
EdgeLuxe
But it is! I have checked it many times, getting the ciphertext length and dividing it block size. The result is an integer. And everything is working perfectly w/o writing to the file, so I believe a bug is there.
EdgeLuxe
Maybe CryptoPP has its own setting for text vs binary? That would be really weird, but maybe worth looking for.
Ben Voigt
Oh, well, I guess it is CryptoPP. I have found way not to use file writing, but thanks very much, mentioned functions helped me other way. Thanks.
EdgeLuxe