Encoding/decoding fails on Linux - c++

I am looking for a bit of help with my encode/decode functions on cross platforms. Currently the code works across windows OS's but fails on linux. Currently it is untested on Mac.
void tradingDialog::on_SaveKeys_clicked()
{
// Encryption properties store iv and password information
EncryptionProperties props;
// Generate a 256 bit random IV from 4 separate 64 bit numbers
props.iv = crypto_random();
props.iv2 = crypto_random();
props.iv3 = crypto_random();
props.iv4 = crypto_random();
// What cipher function do we require?
props.cipher = Algorithm::AES;
// Qstring to string
string password = ui->PasswordInput->text().toUtf8().constData();
// the password used for encryption / decryption
props.password = string(password);
/*========== The main cryptostreampp usage ==========*/
boost::filesystem::path pathAPI = GetDataDir() / "APIcache.txt";
// Create a stream in output mode to create a brand new file called apicache.txt
//CryptoStreamPP stream(pathAPI.string(), props, std::ios::out | std::ios::binary | std::ios::trunc);
CryptoStreamPP stream(pathAPI.string(), props, std::ios::out | std::ios::binary | std::ios::trunc);
// ------------------------------------------------------
// NOTE:
// After creating the stream, there will be a short pause
// as the key stream is initialized. This accounts for
// one million iterations of PBKDF2
// ------------------------------------------------------
// write to the stream as you would a normal fstream. Normally
// you would write a buffer of char data. In this example,
// we write a string which is basically the same thing.
// Stream operator support to be properly added in future.
// qstrings to utf8, add to byteArray and convert to const char for stream
const QByteArray byteArray = (ui->ApiKeyInput->text().toUtf8() + ui->SecretKeyInput->text().toUtf8());
const char *API = byteArray.constData();
stream.write(API, 64);
// make sure stream is flushed before closing it
stream.flush();
stream.close();
}
void tradingDialog::on_LoadKeys_clicked()
{
// Encryption properties store iv and password information
EncryptionProperties props;
// Generate a 256 bit random IV from 4 separate 64 bit numbers
props.iv = crypto_random();
props.iv2 = crypto_random();
props.iv3 = crypto_random();
props.iv4 = crypto_random();
// What cipher function do we require?
props.cipher = Algorithm::AES;
// Qstring to string
string password = ui->PasswordInput->text().toUtf8().constData();
// the password used for encryption / decryption
props.password = string(password);
boost::filesystem::path pathAPI = GetDataDir() / "APIcache.txt";
// Create a stream in input mode to open a file named APIcache.txt
CryptoStreamPP stream(pathAPI.string(), props, std::ios::in | std::ios::binary);
// Read in a buffer of data
{
QString Key = "";
stream.seekg(0);
char buffer[33];
stream.read(buffer, 32);
buffer[32] = '\0';
// Should print out "api key 32 digit"
Key = buffer;
ui->ApiKeyInput->setText(Key);
}
stream.flush();
// now seek to digit 32 and read in api secret
{
QString Secret = "";
stream.seekg(32);
char buffer[33];
stream.read(buffer, 32);
buffer[32] = '\0';
// Should print out "api secret 32 digit"
Secret = buffer;
ui->SecretKeyInput->setText(Secret);
}
stream.flush();
stream.close();
}
When printing "password" and "API" in "on_SaveKeys_clicked()". Both are correct.
When printing "password" in "on_LoadKeys_clicked()" it is correct.
When printing "Key" in "on_LoadKeys_clicked()" it is incorrect.
When printing "Secret" in "on_LoadKeys_clicked()" it is incorrect.
CryptoStreamPP can be found at https://github.com/benhj/CryptoStreamPP/tree/master/cryptostreampp

Related

Crypto++ | HexEncoder sometimes encodes incorrectly | HexEncoder not working consistently | Undefined behaviour?

I have written a simple encrypt & decrypt function and everything works. Except sometimes the HexEncoder seems to encode the cipher far too way small. Therefore the decryption fails. But this rarely happens. I have isolated the bug to the HexEncoder since the log Mismatch on encoding is only present when the problem appears. A bad workaround is to call the encrypt function again, which solves the problem. Not sure why, perhaps because of a newly generated IV but this seems unlikely to me.
Am I doing something wrong, undefined behaviour? Or is this a bug from the HexEncoder?
Encrypt function:
// Encrypt data.
template <typename Encryption> static inline
int encrypt(Encryption& m_enc, str_t& encrypted, const str_t& data) {
// Generate & set iv.
CryptoPP::byte iv [CryptoPP::AES::BLOCKSIZE];
rand.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE);
m_enc.Resynchronize(iv, CryptoPP::AES::BLOCKSIZE);
// Encrypt.
str_t cipher;
cipher.resize(data.len() + CryptoPP::AES::BLOCKSIZE);
cipher.len() = data.len() + CryptoPP::AES::BLOCKSIZE;
CryptoPP::ArraySink encrypt_sink(
(Byte*) cipher.data(),
cipher.len()
);
try {
CryptoPP::StringSource(
data.data(),
true,
new CryptoPP::StreamTransformationFilter(m_enc, new CryptoPP::Redirector(encrypt_sink))
);
} catch (...) {
return crypto::error::encrypt;
}
//print("Cipher 1: ", cipher);
// Set cipher text length now that its known
if (cipher.len() != encrypt_sink.TotalPutLength()) {
print("Mismatch on ciper; expected: ", cipher.len(), " sinked: ", encrypt_sink.TotalPutLength());
}
cipher.resize(encrypt_sink.TotalPutLength());
cipher.len() = encrypt_sink.TotalPutLength();
//print("Cipher 2: ", cipher);
// Encode.
encrypted.resize(cipher.len() * 2);
encrypted.len() = cipher.len() * 2;
CryptoPP::ArraySink encode_sink((Byte*) encrypted.data(), encrypted.len());
try {
CryptoPP::StringSource(
cipher.data(),
true,
new CryptoPP::HexEncoder(
new CryptoPP::Redirector(encode_sink)
)
);
} catch (...) {
encrypted.len() = 0;
return crypto::error::encode;
}
print("Encoded cipher 1: ", encrypted);
// Set encrypted length.
if (encrypted.len() != encode_sink.TotalPutLength()) {
print("BUG -> Mismatch on encoding; expected: ", encrypted.len(), " sinked: ", encode_sink.TotalPutLength());
//str_t shortened;
//shortened.resize(encode_sink.TotalPutLength());
//shortened.concat_no_resize(encrypted.data(), encode_sink.TotalPutLength());
//encrypted.swap(shortened);
return encrypt(m_enc, encrypted, data);
}
encrypted.resize(encode_sink.TotalPutLength());
encrypted.len() = encode_sink.TotalPutLength();
print("Encoded cipher 2: ", encrypted);
// Encode IV.
str_t encoded_iv;
encoded_iv.resize(CryptoPP::AES::BLOCKSIZE * 2);
encoded_iv.len() = CryptoPP::AES::BLOCKSIZE * 2;
CryptoPP::ArraySink iv_sink((Byte*) encoded_iv.data(), encoded_iv.len());
try {
CryptoPP::ArraySource(
iv,
CryptoPP::AES::BLOCKSIZE,
true,
new CryptoPP::HexEncoder(
new CryptoPP::Redirector(iv_sink)
)
);
} catch (...) {
encrypted.len() = 0;
return crypto::error::encode;
}
// Set encoded iv length.
if (encoded_iv.len() != iv_sink.TotalPutLength()) {
print("Mismatch on encoding iv; expected: ", encoded_iv.len(), " sinked: ", iv_sink.TotalPutLength());
}
encoded_iv.resize(iv_sink.TotalPutLength());
encoded_iv.len() = iv_sink.TotalPutLength();
print("Encoded IV: ", encoded_iv);
// Concat IV.
encoded_iv.concat(encrypted);
encrypted.swap(encoded_iv);
return 0;
}
Decrypt function:
// Decrypt data.
template <typename Decryption> static inline
int decrypt(Decryption& m_dec, str_t& decrypted, const str_t& data) {
// Decode.
str_t decoded;
decoded.resize(data.len() / 2);
decoded.len() = data.len() / 2;
try {
CryptoPP::StringSource(
data.data(),
true,
new CryptoPP::HexDecoder(
new CryptoPP::ArraySink((Byte*) decoded.data(), decoded.len())
)
);
} catch (...) {
return crypto::error::decode;
}
// Skip for now.
m_dec.Resynchronize((Byte*) decoded.data(), CryptoPP::AES::BLOCKSIZE);
// Recovered text will be less than cipher text
decrypted.resize(decoded.len() - CryptoPP::AES::BLOCKSIZE);
decrypted.len() = decoded.len() - CryptoPP::AES::BLOCKSIZE;
CryptoPP::ArraySink rs((Byte*) decrypted.data(), decrypted.len());
try {
CryptoPP::StringSource(
(Byte*) decoded.data() + CryptoPP::AES::BLOCKSIZE,
decoded.len(),
true,
new CryptoPP::StreamTransformationFilter(
m_dec,
new CryptoPP::Redirector(rs),
CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::NO_PADDING
)
);
} catch (...) {
decrypted.len() = 0;
return crypto::error::decrypt;
}
// Set recovered text length now that its known
decrypted.resize(rs.TotalPutLength());
decrypted.len() = rs.TotalPutLength();
return 0;
}
Testing:
...
str_t data = "Hello World!", encrypted, decrypted;
aes.encrypt(encrypted, data);
aes.decrypt(decrypted, encrypted);
print("Encrypted: ", encrypted);
print("Decrypted: ", decrypted);
This are the normal logs (without the bug occurence):
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 04A2C4FB074F6ACBA996239224BD5F77
Encoded cipher 2: 04A2C4FB074F6ACBA996239224BD5F77
Encoded IV: 02466AEBCF4AC2066CE2E144FC5B71C8
Encrypted: 02466AEBCF4AC2066CE2E144FC5B71C804A2C4FB074F6ACBA996239224BD5F77
Decrypted: Hello World!
This are the logs when the bug occurs (with the recursive encrypt call commented out):
Decoded: Hello World!
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: C97BFE
BUG -> Mismatch on encoding; expected: 32 sinked: 6
Encoded cipher 2: C97BFE
Encoded IV: 06E05C3DAE3E9D6DAC8971E5C9CA0A1A
Encrypted: 06E05C3DAE3E9D6DAC8971E5C9CA0A1AC97BFE
Decrypted:
This are the logs when the bug occurs with the workaround:
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: A0
BUG -> Mismatch on encoding; expected: 32 sinked: 2
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 883A8A644E5B50067A
BUG -> Mismatch on encoding; expected: 32 sinked: 18
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 58AD2010C761215422F89D42FC2F2396
Encoded cipher 2: 58AD2010C761215422F89D42FC2F2396
Encoded IV: BE361B7D8C9144052220E143AD54CB63
Encrypted: BE361B7D8C9144052220E143AD54CB6358AD2010C761215422F89D42FC2F2396
Decrypted: Hello World!
I don't know what a str_t is, but since it has a .data() method I assume that returns the underlying char*. Since you are not specifying a length in the CryptoPP::StringSource, it assumes the input stops at the first NUL byte. For plaintext input this is often fine but cipher data MAY contain embedded NULs.
Instead, either pass the length of your cipher explicitly:
CryptoPP::StringSource(
cipher.data(),
cipher.len(),
true,
new CryptoPP::HexEncoder(
new CryptoPP::Redirector(encode_sink)
)
);
Or, better, convert the str_t into a std::string that knows its own size and pass that as argument.

Issues reimplementing some C# encryption stuff in C++ using cryptopp

So I have this piece of C# code:
void Decrypt(Stream input, Stream output, string password, int bufferSize) {
using (var algorithm = Aes.Create()) {
var IV = new byte[16];
input.Read(IV, 0, 16);
algorithm.IV = IV;
var key = new Rfc2898DeriveBytes(password, algorithm.IV, 100);
algorithm.Key = key.GetBytes(16);
using(var decryptor = algorithm.CreateDecryptor())
using(var cryptoStream = new CryptoStream(input, decryptor, CryptoStreamMode.Read)) {
CopyStream(cryptoStream, output, bufferSize);
}
}
}
and I am trying to translate this into C++ with CryptoPP.
So this is what I have written:
void decrypt(std::ifstream& in_file, std::ofstream& out_file, std::string_view password, size_t bufSize) {
using namespace CryptoPP;
// Get IV
byte iv[16];
in_file.read(reinterpret_cast<char*>(iv), sizeof(iv));
// Read cypher
std::string cypher;
while (in_file && cypher.size() != bufSize) {
char c;
in_file.read(&c, 1);
cypher.push_back(c);
}
// Get key
byte key[16];
PKCS5_PBKDF2_HMAC<SHA1> pbkdf2;
pbkdf2.DeriveKey(key, sizeof(key), 0, reinterpret_cast<const byte*>(password.data()), password.size(), iv, sizeof(iv), 100);
// Decrypt
CTR_Mode<AES>::Decryption decrypt(key, sizeof(key), iv);
std::string output;
StringSource(cypher, true, new StreamTransformationFilter(decrypt, new StringSink(output)));
// Write output to file
out_file.write(output.data(), output.size());
}
However, from this function, I am only getting back trash data. What could I be doing wrong?
Thanks
Tuxifan!
So I found the solution! First of all, as #mbd mentioned, C# uses CBC by default. Additionally, I need to cut away the rest of the data like this:
while ((cipher.size() % 16) != 0) {
cipher.pop_back();
}

How to add arabic characters in a middle of a file using C++?

I am building an application that will need to add arabic characters in the middle of english file, I build the function as follow:
int main(void) {
std::ifstream mySource("Test2.txt", std::ios::out);
std::filebuf* pbuf = mySource.rdbuf();
std::size_t size = pbuf->pubseekoff(0, mySource.end, mySource.in);
pbuf->pubseekpos(0, mySource.in);
char* buffer = new char[size];
pbuf->sgetn(buffer, size);
mySource.close();
wchar_t* wbuffer = new wchar_t[size];
wbuffer = GetWC(buffer);
setlocale(LC_ALL, "en_GB.utf8");
wbuffer[79] = {0x0041};
std::wofstream outdata2;
outdata2.open("Test6.xml"); // opens the file
outdata2 << wbuffer;
outdata2.close();
return 0;
}
for a text file as follows:
$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (000,000,000) #000000
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E
and expecting to receive
$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (000,000,000) #A00000
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E
although when I put the arabic letter ASCII like:
...
wbuffer[79] = {0x0628};
...
I receive the following:
$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (000,000,000) #
don't know why?!
The function you are using for output will terminate at a null character. Instead you should use ostream::write for the output
outdata2.write(wbuffer, size);
Also since you are doing binary I/O all your files should be opened with the std::ios::binary bit set.
std::ifstream mySource("Test2.txt", std::ios::out|std::ios::binary);
etc.

HMAC on Mountain lion OSX 10.8.3 EXC_CRASH

Looking for a bit of help using OpenSSL's HMAC function. Currently this function is failing on the HMAC call. ONLY for OSX. Both linux and windows os's are working okay.
QString tradingDialog::HMAC_SHA512_SIGNER(QString UrlToSign, QString Secret){
QString retval = "";
QByteArray byteArray = UrlToSign.toUtf8();
const char* URL = byteArray.constData();
QByteArray byteArrayB = Secret.toUtf8();
const char* Secretkey = byteArrayB.constData();
const EVP_MD *md = EVP_sha512();
unsigned char* digest = NULL;
// Be careful of the length of string with the choosen hash engine. SHA1 produces a 20-byte hash value which rendered as 40 characters.
// Change the length accordingly with your choosen hash engine
char mdString[129] = { 0 };
// Using sha512 hash engine here.
digest = HMAC(md, Secretkey, strlen( Secretkey), (unsigned char*) URL, strlen( URL), NULL, NULL);
for(int i = 0; i < 64; i++){
sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
}
retval = mdString;
return retval;
}
You don't say what the problem is on osx, but it looks like you're not nul terminating mdString, so try changing it to
char mdString[129] = { 0 };
The crashlog you linked to shows that your app is aborting because the stack has been corrupted (I assume this happens on exit).
I would say the final sprintf is causing this, as it is adding a nul byte after the end of your mdString array. Try the above modification and see if that helps.
This ought to crash on all platforms, but I guess you got "lucky".

AES encryption C++ (can't decrypt encrypted file)

I'm playing around with AES and write the iv and derived key to the file so that I use it later for decryption...
following function encrypts the file and saves the iv and derived key into the file, it also saves encrypted text into separate file:
void MainWindow::on_button_encrypt() try
{
using namespace std;
using namespace Gtk;
using namespace CryptoPP;
fstream file;
file.open(m_file_name + ".aes", std::ios::out | std::ios::trunc);
if (file.is_open())
{
// get the plain password:
SecByteBlock password;
password_dialog get_passwd(password);
get_passwd.run();
// generate random salt
const int SALT_SIZE = 32;
SecByteBlock salt(SALT_SIZE);
AutoSeededRandomPool prng;
prng.GenerateBlock(salt.BytePtr(), SALT_SIZE);
// derive a key from password and salt:
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
PKCS5_PBKDF2_HMAC<SHA512> gen;
gen.DeriveKey(key.BytePtr(), key.size(), 1, password.BytePtr(), password.size(), salt.BytePtr(), salt.size(), 200);
// genereate random iv:
SecByteBlock iv(AES::BLOCKSIZE);
OS_GenerateRandomBlock(false, iv.BytePtr(), AES::BLOCKSIZE);
// encrypt plain text:
AES::Encryption enc(key.BytePtr(), AES::DEFAULT_KEYLENGTH);
CBC_Mode_ExternalCipher::Encryption mode(enc, iv);
string cipher_text;
StringSink* sink = new StringSink(cipher_text);
StreamTransformationFilter encryptor(mode, sink);
string plain_text = m_file_buffer->get_text();
encryptor.Put(reinterpret_cast<const byte*>(plain_text.c_str()), plain_text.length() + 1);
encryptor.MessageEnd();
// write the result:
file << cipher_text.c_str();
file.close();
file.open(m_file_name + ".key", std::ios::out | std::ios::trunc);
file << key << '\n' << iv;
file.close();
}
}
// catch ...
The above function writes the iv and derived key to the file.
0000000002F9F140
0000000002F9F4E0
Following function is supposed to read the file and decrypt it by using the *.key file:
void MainWindow::on_button_decrypt()
{
using namespace std;
using namespace CryptoPP;
Gtk::MessageDialog info("Info");
fstream file;
file.open("decrypted.txt", ios::out | ios::trunc);
if (file.is_open())
{
// read the key:
fstream stream;
string input;
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
SecByteBlock iv(AES::BLOCKSIZE);
stream.open("test.txt.key", std::ios::in);
if (stream.is_open())
{
getline(stream, input);
key.Assign(reinterpret_cast<const byte*>(input.c_str()), input.size());
input.clear();
getline(stream, input);
iv.Assign(reinterpret_cast<const byte*>(input.c_str()), input.size());
}
else
{
info.set_secondary_text("can't read key file");
info.run();
return;
}
// decrypt:
AES::Decryption dec(key, AES::DEFAULT_KEYLENGTH);
CBC_Mode_ExternalCipher::Decryption mode(dec, iv);
string plain_text;
StringSink* sink = new StringSink(plain_text);
StreamTransformationFilter decryptor(mode, sink);
try
{
// get encrypted text from buffer (obtained from other function):
string cipher_text = m_file_buffer->get_text();
decryptor.Put(reinterpret_cast<const byte*>(cipher_text.c_str()), cipher_text.size());
decryptor.MessageEnd();
file << plain_text;
file.close();
}
catch (CryptoPP::Exception& ex)
{
info.set_secondary_text(ex.what());
info.run();
}
}
}
CryptoPP trows an exception while decrypting saying this:
FileTransformationFilter: ciphertext length is not multiple of a block size.
How ever it does not throw anything if decryption process is done in first function ( on_btn_encrypt() ) no file is read there, so it looks I'm having problem with reading encrypted file but have no idea how?
This is content of encrypted file:
&`OôÍoYjÜMe×Q°Þ
plaintext is:
some text
Do you see what am I missing here? thanks so much!
EDIT:
here are modification which solved the problem as sugested by Qmick and Artjom:
ENCRYPTION
// encrypt the file:
AES::Encryption enc(key.BytePtr(), AES::DEFAULT_KEYLENGTH);
CBC_Mode_ExternalCipher::Encryption mode(enc, iv);
string file = m_file_name + ".aes";
FileSink* sink = new FileSink(file.c_str());
StreamTransformationFilter encryptor(mode, sink);
string plain_text = m_file_buffer->get_text();
encryptor.Put(reinterpret_cast<const byte*>(plain_text.c_str()), plain_text.length());
encryptor.MessageEnd();
// write the key:
file = m_file_name + ".key";
FileSink* out = new FileSink(file.c_str());
Base64Encoder* base64_enc = new Base64Encoder;
base64_enc->Attach(out);
base64_enc->Put(key.BytePtr(), key.size());
base64_enc->MessageEnd();
// write the iv:
file = m_file_name + ".iv";
FileSink* out2 = new FileSink(file.c_str());
Base64Encoder* base64_enc2 = new Base64Encoder;
base64_enc2->Attach(out2);
base64_enc2->Put(iv.BytePtr(), iv.size());
base64_enc2->MessageEnd();
DECRYPTION:
//
// read key
//
string store_key;
StringSink* string_key_in = new StringSink(store_key);
Base64Decoder* base64_key_dec = new Base64Decoder(string_key_in);
string file = "test.txt.key";
FileSource* file_key_in = new FileSource(file.c_str(), true, base64_key_dec);
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
key.Assign(reinterpret_cast<const byte*>(store_key.c_str()), store_key.size());
//
// read iv
//
string store_iv;
StringSink* string_iv_in = new StringSink(store_iv);
Base64Decoder* base64_iv_dec = new Base64Decoder(string_iv_in);
file = "test.txt.iv";
FileSource* file_iv_in = new FileSource(file.c_str(), true, base64_iv_dec);
SecByteBlock iv(AES::BLOCKSIZE);
iv.Assign(reinterpret_cast<const byte*>(store_iv.c_str()), store_iv.size());
// read ciphertext:
string store_ciphertext;
StringSink* ciphertext_in = new StringSink(store_ciphertext);
file = m_file_name;
FileSource* file_ciphertext_in = new FileSource(file.c_str(), true, ciphertext_in);
SecByteBlock cipher_text;
cipher_text.Assign(reinterpret_cast<const byte*>(store_ciphertext.c_str()), store_ciphertext.size());
//
// decrypt:
//
AES::Decryption dec(key, AES::DEFAULT_KEYLENGTH);
CBC_Mode_ExternalCipher::Decryption mode(dec, iv);
file = "decrypted.txt";
FileSink* sink = new FileSink(file.c_str());
StreamTransformationFilter decryptor(mode, sink);
decryptor.Put(cipher_text.BytePtr(), cipher_text.size());
decryptor.MessageEnd();
There is no guarentee cipher text will not contain escape sequence like \n, which make getline() get wrong string(truncated).
So it is recommended to transform the cipher text to hex or something else to avoid escape sequence.