Writing binary data to private ofstream produces unexpected result - c++

I am working on a class that will read and write binary data to/from a file.
I am testing it by sending an 'a'. When I sent it to cout and it worked. I sent it to a text file, it sent î.
What is different about the ofstream that is causing this?
#include <iostream>
#include "bin2.h"
using namespace std;
int main()
{
bin myBin("e:\\Temp\\test.txt");
char data[1];
data[0] = 'a';
myBin.write(data, 1);
system("PAUSE");
return 0;
}
bin2.h
#pragma once
#include <fstream>
class bin
{
std::ofstream outfile;
std::ifstream infile;
std::filebuf *outBuff, *inBuff;
int buffSize = 5;
char* buffer;
//0 = input, 1 = output, 2 = ready to delete, 3 = unitialized
char mode = 3;
public:
//constructor with no parameters
bin(){ ; };
//if 'this' is constructed with a file, call init() to set object up
bin(char fileName[])
{
init(fileName);
};
void init(char fileName[])
{
try
{
//check if isUninitialized
if (!isUninitialized())
return;
//open the file and make sure it opened
outfile.open(fileName);
infile.open(fileName);
if (!outfile.is_open() || !infile.is_open())
throw std::runtime_error((std::string)"Failed to open file " + fileName);
//create buffer, get pointers to filebuffs, and set them to the new buffer
buffer = new char[buffSize];
outBuff = outfile.rdbuf();
outBuff->pubsetbuf(buffer, buffSize);
inBuff = infile.rdbuf();
inBuff->pubsetbuf(buffer, buffSize);
//set mode to input
mode = 0;
return
}
//if any exceptions were thrown, call the cleanup then rethrow the exception so
// the caller can handle the error as well
catch (std::exception & ex) {
cleanup();
throw ex;
}
};
virtual ~bin(){
cleanup();
};
//methods to check mode
bool modeIsInput(){ return mode == 0; };
bool modeIsOutput(){ return mode == 1; };
bool isReadyToDel(){ return mode == 2; };
bool isUninitialized(){ return mode == 3; };
std::string getMode(){
switch (mode) {
case 0: return "input";
case 1: return "output";
case 2: return "readyToDel";
case 3: return "unitialized";
default: return "invalid";
}
};
//method to write data into the object
bin * write(char data[], int length){
//make sure object is in input mode
if (mode != 0)
throw std::runtime_error("Cannot write to object when not in input mode. Current mode = " + getMode());
//DEBUG
std::cout << "Writing data: ";
std::cout.write(data, length);
std::cout << std::endl;
//end of DEBUG
//write data and return pointer to object
outfile.write(data, length);
return this;
};
private:
void cleanup()
{
delete buffer;
outfile.close();
infile.close();
//change mode to readyToDel
mode = 2;
};
};

You have:
bool modeIsInput(){ return mode = 0 ? true : false; };
bool modeIsOutput(){ return mode = 1 ? true : false; };
bool isReadyToDel(){ return mode = 2 ? true : false; };
bool isUninitialized(){ return mode = 3 ? true : false; };
I am sure you meant:
bool modeIsInput(){ return mode == 0 ? true : false; };
bool modeIsOutput(){ return mode == 1 ? true : false; };
bool isReadyToDel(){ return mode == 2 ? true : false; };
bool isUninitialized(){ return mode == 3 ? true : false; };
or better still.
bool modeIsInput(){ return mode == 0; };
bool modeIsOutput(){ return mode == 1; };
bool isReadyToDel(){ return mode == 2; };
bool isUninitialized(){ return mode == 3; };
I don't know whether fixing those will fix all your problems.

I copied my code as is and saved it it in a different file. The problem is solved, but I have no idea what caused it.

Related

Custom input stream. Stream buffer and underflow method

To understand how input streams work I designed 2 of the following classes:
#include <iostream>
class my_streambuf: public std::streambuf
{
private:
std::streambuf* buffer;
char ch;
protected:
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if (result != traits_type::eof())
{
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
public:
my_streambuf(std::streambuf* buffer) : buffer(buffer) {};
virtual ~my_streambuf() {};
};
class my_istream: public std::istream
{
public:
my_istream(std::istream& stream) : std::istream(new my_streambuf(stream.rdbuf())) {};
virtual ~my_istream()
{
delete rdbuf();
}
};
int main()
{
char s[32];
my_istream is(std::cin);
is >> s;
std::cout << s;
return 0;
}
Which work fine, until I change the logic of underflow method. The primary goal is to save data in c-string valiable s which differs from user-input. To make a simple test, I changed the underflow method to be the following:
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if (result != traits_type::eof())
{
result = traits_type::to_int_type('+'); // <-- this was added
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
With the idea being to make the method return only + symbols instead of user-input chars.
So for example if input is 123, I expect +++ to be stored in variable s.
And that does not work. Console hangs as if it is waiting more input. Only a certain amount of keypressing (or sending EOF) helps.
What am I missing here?
As was pointed out by #ferosekhanj, the problem was the missing newline, which was not returned by the modified version of underflow to the caller. So in order for the code to work properly it has to be returned. This version of the method works fine.
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if ((result != traits_type::eof()) && !traits_type::eq(traits_type::to_char_type(result), '\n'))
{
result = traits_type::to_int_type('+');
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
From my old C++ experience a stream buf is the underlying buffer for the stream. When the stream needs more data it calls underflow. Inside this method you are suppose to read from your source and setg. When the stream has data to be written back to the source it calls overflow. Inside this method you read from the stream,write back to your source and setp. For example if you are reading the data from a socket in your streambuf
socketbuf::int_type socketbuf::underflow(){
int bytesRead = 0;
try{
bytesRead = soc->read(inbuffer,BUFFER_SIZE-1,0);
if( bytesRead <= 0 ){
return traits_type::eof();
}
}catch(IOException ioe){
cout<<"Unable to read data"<<endl;
return traits_type::eof();
}
setg(inbuffer,inbuffer,inbuffer+bytesRead);
return traits_type::to_int_type(inbuffer[0]);
}
socketbuf::int_type socketbuf::overflow(socketbuf::int_type c){
int bytesWritten = 0;
try{
if(pptr() - pbase() > 0){
bytesWritten = soc->write(pbase(),(pptr() - pbase()),0);
if( bytesWritten <= 0 ) return traits_type::not_eof(c);
}
}catch(IOException ioe){
cout<<"Unable to write data"<<endl;
return traits_type::eof();
}
outbuffer[0] = traits_type::to_char_type(c);
setp(outbuffer,outbuffer+1,outbuffer+BUFFER_SIZE);
return traits_type::not_eof(c);
}
Now coming to your code, you added
result = traits_type::to_int_type('+'); // <-- this was added
A stream reads a string until it sees a LF(line feed). So when the LF character come you are over writing that with a '+' so the stream will wait (for LF) forever.By adding this check your code should do what you are expecting. output '+++' if you input 'abc'
if (result != 10)// <-- add this in addition
result = traits_type::to_int_type('+'); // <-- this was added
Hope it helps you.

error: no match for 'operator=' in 'myFile = SDClass::open(const char*, uint8_t)(((const char*)"test.txt"), 19u)'

FILE.CPP: I am using this file and to use this I have created object "myFile"
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#include <SD.h>
/* for debugging file open/close leaks
uint8_t nfilecount=0;
*/
File::File(SdFile f,
const char *n) {
// oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
_file=(SdFile *)malloc(sizeof(SdFile));
if (_file) {
memcpy(_file, &f, sizeof(SdFile));
strncpy(_name, n, 12);
_name[12]=0;
/* for debugging file open/close leaks
nfilecount++;
Serial.print("Created \"");
Serial.print(n);
Serial.print("\": ");
Serial.println(nfilecount, DEC);
*/
}
}
File::File(void) {
_file=0;
_name[0]=0;
//Serial.print("Created empty file object");
}
File::~File(void) {
// Serial.print("Deleted file object");
}
// returns a pointer to the file name
char *File::name(void) {
return _name;
}
// a directory is a special type of file
boolean File::isDirectory(void) {
return (_file && _file->isDir());
}
size_t File::write(uint8_t val) {
return write(&val, 1);
}
size_t File::write(const uint8_t *buf,
size_t size) {
size_t t;
if (!_file) {
setWriteError();
return 0;
}
_file->clearWriteError();
t=_file->write(buf,
size);
if (_file->getWriteError()) {
setWriteError();
return 0;
}
return t;
}
int File::peek() {
if (! _file) return 0;
int c=_file->read();
if (c !=-1) _file->seekCur(-1);
return c;
}
int File::read() {
if (_file) return _file->read();
return -1;
}
// buffered read for more efficient, high speed reading
int File::read(void *buf,
uint16_t nbyte) {
if (_file) return _file->read(buf, nbyte);
return 0;
}
int File::available() {
if (! _file) return 0;
uint32_t n=size() - position();
return n > 0X7FFF ? 0X7FFF: n;
}
void File::flush() {
if (_file) _file->sync();
}
boolean File::seek(uint32_t pos) {
if (! _file) return false;
return _file->seekSet(pos);
}
uint32_t File::position() {
if (! _file) return -1;
return _file->curPosition();
}
uint32_t File::size() {
if (! _file) return 0;
return _file->fileSize();
}
void File::close() {
if (_file) {
_file->close();
free(_file);
_file=0;
/* for debugging file open/close leaks
nfilecount--;
Serial.print("Deleted ");
Serial.println(nfilecount, DEC);
*/
}
}
File::operator bool() {
if (_file) return _file->isOpen();
return false;
}
I am trying to create a SD card read write package using Simulink. What happens is here is I would take SD.cpp and SD.h and other required files and create a wrapper file which consists of required operation (i.e. reading and writing to SD card) and I am getting the following errors
SDcard_wrapper.cpp: In function 'void SDcard_Update_wrapper(const boolean_T*, real_T*, const uint8_T*, int_T)':
./SDcard_wrapper.cpp:113: error: no match for 'operator=' in 'myFile = SDClass::open(const char*, uint8_t)(((const char*)"test.txt"), 19u)'
C:\Users\Samanth\Documents\MATLAB\newSD-trail/utility/SdFat.h:135: note: candidates are: SdFile& SdFile::operator=(const SdFile&)
./SDcard_wrapper.cpp:123: error: could not convert 'myFile' to 'bool'
Following is the SDcard_wrapper.cpp error locations:
myFile = SD.open("test.txt", FILE_WRITE); //This is the line where I am getting 1st error
if (myFile) // this is the point where I am getting the error: could not convert 'myFile' to 'bool'
{
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
File SDClass::open(const char* filepath, uint8_t mode) {
/*
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
*/
int pathidx;
// do the interative search
SdFile parentdir = getParentDir(filepath, &pathidx);
// no more subdirs!
filepath += pathidx;
if (!filepath[0]) {
// it was the directory itself!
return File(parentdir, "/");
}
// Open the file itself
SdFile file;
// failed to open a subdir!
if (!parentdir.isOpen())
return File();
// there is a special case for the Root directory since its a static dir
if (parentdir.isRoot()) {
if (!file.open(SD.root, filepath, mode)) {
// failed to open the file :(
return File();
}
// dont close the root!
} else {
if (!file.open(parentdir, filepath, mode)) {
return File();
}
// close the parent
parentdir.close();
}
if (mode & (O_APPEND | O_WRITE))
file.seekSet(file.fileSize());
return File(file, filepath);
}
class SdFile : public Print // This is the line pointed above in the second error just for information SdFat.h:135: note: candidates are: SdFile& SdFile::operator=(const SdFile&)
{
public:
/** Create an instance of SdFile. */
SdFile(void): type_(FAT_FILE_TYPE_CLOSED) {}
/**
* writeError is set to true if an error occurs during a write().
* Set writeError to false before calling print() and/or write() and check
* for true after calls to print() and/or write().
*/
//bool writeError;
/**
* Cancel unbuffered reads for this file.
* See setUnbufferedRead()
*/
void clearUnbufferedRead(void) {
flags_ &= ~F_FILE_UNBUFFERED_READ;
}
uint8_t close(void);
uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
uint8_t createContiguous(SdFile* dirFile, const char* fileName, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster(void) const {
return curCluster_;
}
/** \return The current position for a file or directory. */
uint32_t curPosition(void) const {
return curPosition_;
}
static void dateTimeCallback(void(*dateTime)(uint16_t* date, uint16_t* time)) {
dateTime_ = dateTime;
}
/**
* Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel(void) {
// use explicit zero since NULL is not defined for Sanguino
dateTime_ = 0;
}

std::cin: empty the input buffer without blocking

A followup to this question.
How can I clear the input buffer?
sleep(2);
// clear cin
getchar();
I only want the character that was typed in last, I want to discard any input that was put in while the program was asleep. Is there a way to do that?
Also, I don't want to wait actively to clear cin, thus I can't use ignore().
My approach would have been to get the current size of the buffer and if it's not equal to 0, silently empty the buffer. However, I have not found a way to get the size of the buffer. std::cin.rdbuf()->in_avail() always returns 0 for me. peek() also waits actively.
I don't want to use ncurses.
Having a system supporting tcgetattr/tcsetattr:
#include <iostream>
#include <stdexcept>
#include <termios.h>
#include <unistd.h>
class StdInput
{
// Constants
// =========
public:
enum {
Blocking = 0x01,
Echo = 0x02
};
// Static
// ======
public:
static void clear() {
termios attributes = disable_attributes(Blocking);
while(std::cin)
std::cin.get();
std::cin.clear();
set(attributes);
}
// StdInput
// ========
public:
StdInput()
: m_restore(get())
{}
~StdInput()
{
set(m_restore, false);
}
void disable(unsigned flags) { disable_attributes(flags); }
void disable_blocking() { disable_attributes(Blocking); }
void restore() { set(m_restore); }
private:
static termios get() {
const int fd = fileno(stdin);
termios attributes;
if(tcgetattr(fd, &attributes) < 0) {
throw std::runtime_error("StdInput");
}
return attributes;
}
static void set(const termios& attributes, bool exception = true) {
const int fd = fileno(stdin);
if(tcsetattr(fd, TCSANOW, &attributes) < 0 && exception) {
throw std::runtime_error("StdInput");
}
}
static termios disable_attributes(unsigned flags) {
termios attributes = get();
termios a = attributes;
if(flags & Blocking) {
a.c_lflag &= ~ICANON;
a.c_cc[VMIN] = 0;
a.c_cc[VTIME] = 0;
}
if(flags & Echo) {
a.c_lflag &= ~ECHO;
}
set(a);
return attributes;
}
termios m_restore;
};
int main()
{
// Get something to ignore
std::cout << "Ignore: ";
std::cin.get();
// Do something
StdInput::clear();
std::cout << " Input: ";
std::string line;
std::getline(std::cin, line);
std::cout << "Output: " << line << std::endl;
return 0;
}

could not determine size if fclose not done twice

I'm made a File class that is a sort of wrapper of the FILE type and added some methods.
This is the code of my file class :
#include <Fs/File.h>
File::File(Path& p):
m_path(p),
m_openned(false)
{
}
int File::open(const string& mode)
{
m_f = new FILE;
fopen(m_path, mode.c_str());
if (m_f == NULL)
{
m_openned = false;
return -1;
}
m_openned = true;
return 0;
}
bool File::exists()
{
FILE* file;
if (file = fopen(m_path, "r"))
{
fclose(file);
return true;
}
fclose(file);
return false;
}
int File::flush(){
return fflush(m_f);
}
int File::remove()
{
return ::remove(m_path);
}
int File::close()
{
if (isOpenned())
{
m_openned = false;
return fclose(m_f);
}
return 0;
}
long File::getSize()
{
struct stat file_status;
if(!this->exists())
return -1;
if (stat(m_path, &file_status) < 0)
{
return -1;
}
return file_status.st_size;
}
FileMode File::getMode()
{
struct stat file_status;
if (stat(m_path, &file_status) < 0)
{
return FileMode(-1);
}
return FileMode(file_status.st_mode);
}
Path File::getPath()
{
return m_path;
}
bool File::isOpenned()
{
return m_openned;
}
int File::setMode(FileMode& mode)
{
return chmod(m_path, mode);
}
int File::renameTo(File& f)
{
if (f.exists() || !this->exists())
return -1;
return rename( m_path , f.getPath());
}
int File::copyTo(File& to)
{
char ch;
this->close();
this->open(FileTypes::READ);
to.close();
to.open(FileTypes::WRITE);
while (!this->eof())
{
ch = this->readc();
if (ch == -1)
return 0;
if (!to.eof())
to.writec(ch);
}
if (this->close() < 0)
{
return -1;
}
if (to.close() < 0)
{
return -1;
}
return 0;
}
int File::readc()
{
if (!isOpenned())
return FileTypes::ENDOFFILE;
char c = fgetc(m_f);
if (ferror(m_f))
{
return FileTypes::ENDOFFILE;
}
return c;
}
int File::writec(char c)
{
if (!isOpenned())
return -1;
fputc(c, m_f);
if (ferror(m_f))
{
return FileTypes::ENDOFFILE;
}
return 0;
}
bool File::eof()
{
if (!isOpenned())
return true;
return feof(m_f);
}
I made some tests and I have a kind of problem
Path p1("test.txt");
Path p2("temp.txt");
File f1(p1);
File f2(p2);
assert(f1.open(FileTypes::READ) == 0);
assert(f1.exists() == true);
assert(f1.close() == 0);
cout<<"Mode of f1 "<<f1.getMode().getStringMode()<<endl;
cout<<"Path of f1 "<<f1.getPath().getAbsolutePath()<<endl;
cout<<"Size of f1 "<<f1.getSize()<<endl;
assert(f2.exists() == false);
assert(f1.copyTo(f2) == 0);
//#####################################
// If I comment f2.close() the
// assert(f1.getSize() == f2.getSize()) test fails and
// f2.getSize() == 0
##########################################
f2.close();
assert(f2.exists() == true);
assert(f1.getSize() == f2.getSize());
I couldn't figure why this f2.close is needed because I did a close in the copyTo method.
Can someone help me ?
Thank you in advance.
Ben
In File::copyTo:
if (ch == -1)
return 0;
you are jumping out of the function without properly closing the files. When the target file is not closed, it's contents is probably not sent to the OS, which later reports bogus filesize.
fclose flushes the stream. My guess is, is that without closing the file, the stream has not been fully written, so the sizes are different. Consider adding fflush(to); at the end of your copyTo method to ensure everything has been written.
You have multiple exits from the copyTo function which doesn't ensure that you actually close the file. It looks to me that you may be exiting early from the copyTo function and that the intended close isn't executing
while (!this->eof())
{
ch = this->readc();
if (ch == -1)
return 0;
if (!to.eof())
to.writec(ch);
}
when you hit the end of the file you will get EOF which in my os (windows) it is -1, which would cause you to return 0 here, and skip the close call.

Can anyone explain why my crypto++ decrypted file is 16 bytes short?

In order that I might feed AES encrypted text as an std::istream to a parser component I am trying to create a std::streambuf implementation wrapping the vanilla crypto++ encryption/decryption.
The main() function calls the following functions to compare my wrapper with the vanilla implementation:
EncryptFile() - encrypt file using my streambuf implementation
DecryptFile() - decrypt file using my streambuf implementation
EncryptFileVanilla() - encrypt file using vanilla crypto++
DecryptFileVanilla() - decrypt file using vanilla crypto++
The problem is that whilst the encrypted files created by EncryptFile() and EncryptFileVanilla() are identical. The decrypted file created by DecryptFile() is incorrect being 16 bytes short of that created by DecryptFileVanilla(). Probably not coincidentally the block size is also 16.
I think the issue must be in CryptStreamBuffer::GetNextChar(), but I've been staring at it and the crypto++ documentation for hours.
Can anybody help/explain?
Any other comments about how crummy or naive my std::streambuf implementation are also welcome ;-)
Thanks,
Tom
// Runtime Includes
#include <iostream>
// Crypto++ Includes
#include "aes.h"
#include "modes.h" // xxx_Mode< >
#include "filters.h" // StringSource and
// StreamTransformation
#include "files.h"
using namespace std;
class CryptStreamBuffer: public std::streambuf {
public:
CryptStreamBuffer(istream& encryptedInput, CryptoPP::StreamTransformation& c);
CryptStreamBuffer(ostream& encryptedOutput, CryptoPP::StreamTransformation& c);
~CryptStreamBuffer();
protected:
virtual int_type overflow(int_type ch = traits_type::eof());
virtual int_type uflow();
virtual int_type underflow();
virtual int_type pbackfail(int_type ch);
virtual int sync();
private:
int GetNextChar();
int m_NextChar; // Buffered character
CryptoPP::StreamTransformationFilter* m_StreamTransformationFilter;
CryptoPP::FileSource* m_Source;
CryptoPP::FileSink* m_Sink;
}; // class CryptStreamBuffer
CryptStreamBuffer::CryptStreamBuffer(istream& encryptedInput, CryptoPP::StreamTransformation& c) :
m_NextChar(traits_type::eof()),
m_StreamTransformationFilter(0),
m_Source(0),
m_Sink(0) {
m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c, 0, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING);
m_Source = new CryptoPP::FileSource(encryptedInput, false, m_StreamTransformationFilter);
}
CryptStreamBuffer::CryptStreamBuffer(ostream& encryptedOutput, CryptoPP::StreamTransformation& c) :
m_NextChar(traits_type::eof()),
m_StreamTransformationFilter(0),
m_Source(0),
m_Sink(0) {
m_Sink = new CryptoPP::FileSink(encryptedOutput);
m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c, m_Sink, CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING);
}
CryptStreamBuffer::~CryptStreamBuffer() {
if (m_Sink) {
delete m_StreamTransformationFilter;
// m_StreamTransformationFilter owns and deletes m_Sink.
}
if (m_Source) {
delete m_Source;
// m_Source owns and deletes m_StreamTransformationFilter.
}
}
CryptStreamBuffer::int_type CryptStreamBuffer::overflow(int_type ch) {
return m_StreamTransformationFilter->Put((byte)ch);
}
CryptStreamBuffer::int_type CryptStreamBuffer::uflow() {
int_type result = GetNextChar();
// Reset the buffered character
m_NextChar = traits_type::eof();
return result;
}
CryptStreamBuffer::int_type CryptStreamBuffer::underflow() {
return GetNextChar();
}
CryptStreamBuffer::int_type CryptStreamBuffer::pbackfail(int_type ch) {
return traits_type::eof();
}
int CryptStreamBuffer::sync() {
// TODO: Not sure sync is the correct place to be doing this.
// Should it be in the destructor?
if (m_Sink) {
m_StreamTransformationFilter->MessageEnd();
// m_StreamTransformationFilter->Flush(true);
}
return 0;
}
int CryptStreamBuffer::GetNextChar() {
// If we have a buffered character do nothing
if (m_NextChar != traits_type::eof()) {
return m_NextChar;
}
// If there are no more bytes currently available then pump the source
if (m_StreamTransformationFilter->MaxRetrievable() == 0) {
m_Source->Pump(1024);
}
// Retrieve the next byte
byte nextByte;
size_t noBytes = m_StreamTransformationFilter->Get(nextByte);
if (0 == noBytes) {
return traits_type::eof();
}
// Buffer up the next character
m_NextChar = nextByte;
return m_NextChar;
}
void InitKey(byte key[]) {
key[0] = -62;
key[1] = 102;
key[2] = 78;
key[3] = 75;
key[4] = -96;
key[5] = 125;
key[6] = 66;
key[7] = 125;
key[8] = -95;
key[9] = -66;
key[10] = 114;
key[11] = 22;
key[12] = 48;
key[13] = 111;
key[14] = -51;
key[15] = 112;
}
/** Decrypt using my CryptStreamBuffer */
void DecryptFile(const char* sourceFileName, const char* destFileName) {
ifstream ifs(sourceFileName, ios::in | ios::binary);
ofstream ofs(destFileName, ios::out | ios::binary);
byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
InitKey(key);
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption decryptor(key, sizeof(key));
if (ifs) {
if (ofs) {
CryptStreamBuffer cryptBuf(ifs, decryptor);
std::istream decrypt(&cryptBuf);
int c;
while (EOF != (c = decrypt.get())) {
ofs << (char)c;
}
ofs.flush();
}
else {
std::cerr << "Failed to open file '" << destFileName << "'." << endl;
}
}
else {
std::cerr << "Failed to open file '" << sourceFileName << "'." << endl;
}
}
/** Encrypt using my CryptStreamBuffer */
void EncryptFile(const char* sourceFileName, const char* destFileName) {
ifstream ifs(sourceFileName, ios::in | ios::binary);
ofstream ofs(destFileName, ios::out | ios::binary);
byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
InitKey(key);
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption encryptor(key, sizeof(key));
if (ifs) {
if (ofs) {
CryptStreamBuffer cryptBuf(ofs, encryptor);
std::ostream encrypt(&cryptBuf);
int c;
while (EOF != (c = ifs.get())) {
encrypt << (char)c;
}
encrypt.flush();
}
else {
std::cerr << "Failed to open file '" << destFileName << "'." << endl;
}
}
else {
std::cerr << "Failed to open file '" << sourceFileName << "'." << endl;
}
}
/** Decrypt using vanilla crypto++ */
void DecryptFileVanilla(const char* sourceFileName, const char* destFileName) {
byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
InitKey(key);
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption decryptor(key, sizeof(key));
CryptoPP::FileSource(sourceFileName, true,
new CryptoPP::StreamTransformationFilter(decryptor,
new CryptoPP::FileSink(destFileName), CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING
) // StreamTransformationFilter
); // FileSource
}
/** Encrypt using vanilla crypto++ */
void EncryptFileVanilla(const char* sourceFileName, const char* destFileName) {
byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
InitKey(key);
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption encryptor(key, sizeof(key));
CryptoPP::FileSource(sourceFileName, true,
new CryptoPP::StreamTransformationFilter(encryptor,
new CryptoPP::FileSink(destFileName), CryptoPP::BlockPaddingSchemeDef::PKCS_PADDING
) // StreamTransformationFilter
); // FileSource
}
int main(int argc, char* argv[])
{
EncryptFile(argv[1], "encrypted.out");
DecryptFile("encrypted.out", "decrypted.out");
EncryptFileVanilla(argv[1], "encrypted_vanilla.out");
DecryptFileVanilla("encrypted_vanilla.out", "decrypted_vanilla.out");
return 0;
}
After working with a debug build of crypto++ it turns out that what was missing was a call to the StreamTransformationFilter advising it that there would be nothing more coming from the Source and that it should wrap up the processing of the final few bytes, including the padding.
In CryptStreamBuffer::GetNextChar():
Replace:
// If there are no more bytes currently available then pump the source
if (m_StreamTransformationFilter->MaxRetrievable() == 0) {
m_Source->Pump(1024);
}
With:
// If there are no more bytes currently available from the filter then
// pump the source.
if (m_StreamTransformationFilter->MaxRetrievable() == 0) {
if (0 == m_Source->Pump(1024)) {
// This seems to be required to ensure the final bytes are readable
// from the filter.
m_StreamTransformationFilter->ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
}
}
I make no claims that this is the best solution, just one I discovered by trial and error that appears to work.
If your input buffer is not a multiplicity of a 16-byte block, you need to stuff the last block with dummy bytes. If the last block is less than 16 bytes it is dropped by crypto++ and not encrypted. When decrypting, you need to truncate the dummy bytes.
That 'another way' you are referring to, already does the addition and truncation for you.
So what should be the dummy bytes, to know how many of them there is, thus should be truncated? I use the following pattern: fill each byte with the value of dummies count.
Examples: You need to add 8 bytes? set them to 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08. You need to add 3 bytes? set them to 0x03, 0x03, 0x03 etc.
When decrypting, get the value of last byte of the output buffer. Assume it is N. Check, if the values last N bytes are equal to N. Truncate, if true.
UPDATE:
CryptStreamBuffer::CryptStreamBuffer(istream& encryptedInput, CryptoPP::StreamTransformation& c) :
m_NextChar(traits_type::eof()),
m_StreamTransformationFilter(0),
m_Source(0),
m_Sink(0) {
m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c, 0, CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING);
m_Source = new CryptoPP::FileSource(encryptedInput, false, m_StreamTransformationFilter);
}
CryptStreamBuffer::CryptStreamBuffer(ostream& encryptedOutput, CryptoPP::StreamTransformation& c) :
m_NextChar(traits_type::eof()),
m_StreamTransformationFilter(0),
m_Source(0),
m_Sink(0) {
m_Sink = new CryptoPP::FileSink(encryptedOutput);
m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c, m_Sink, CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING);
}
Setting the ZEROS_PADDING made your code working (tested on text files). However why it does not work with DEFAULT_PADDING - I did not find the cause yet.