title: Can anyone explain why my crypto++ decrypted file is 16 bytes short?
body: <p>In order that I might feed AES encrypted text as an <code>std::istream</code> to a parser component I am trying to create a <code>std::streambuf</code> implementation wrapping the vanilla crypto++ encryption/decryption.</p>
  
  <p>The <code>main()</code> function calls the following functions to compare my wrapper with the vanilla implementation:</p>
  
  <ul>
  <li><code>EncryptFile()</code> - encrypt file using my streambuf implementation</li>
  <li><code>DecryptFile()</code> - decrypt file using my streambuf implementation</li>
  <li><code>EncryptFileVanilla()</code> - encrypt file using vanilla crypto++</li>
  <li><code>DecryptFileVanilla()</code> - decrypt file using vanilla crypto++</li>
  </ul>
  
  <p>The problem is that whilst the encrypted files created by <code>EncryptFile()</code> and <code>EncryptFileVanilla()</code> are identical. The decrypted file created by <code>DecryptFile()</code> is incorrect being 16 bytes short of that created by <code>DecryptFileVanilla()</code>. Probably not coincidentally the block size is also 16.</p>
  
  <p>I think the issue must be in <code>CryptStreamBuffer::GetNextChar()</code>, but I've been staring at it and the crypto++ documentation for hours.</p>
  
  <p>Can anybody help/explain?</p>
  
  <p>Any other comments about how crummy or naive my <code>std::streambuf</code> implementation are also welcome ;-)</p>
  
  <p>Thanks,</p>
  
  <p>Tom</p>
  
  <pre><code>// Runtime Includes
  #include &lt;iostream&gt;
  
  // Crypto++ Includes
  #include "aes.h"
  #include "modes.h"      // xxx_Mode&lt; &gt;
  #include "filters.h"    // StringSource and
                          // StreamTransformation
  #include "files.h"
  
  using namespace std;
  
  class CryptStreamBuffer: public std::streambuf {
  
  public:
  
      CryptStreamBuffer(istream&amp; encryptedInput, CryptoPP::StreamTransformation&amp; c);
  
      CryptStreamBuffer(ostream&amp; encryptedOutput, CryptoPP::StreamTransformation&amp; 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&amp; encryptedInput, CryptoPP::StreamTransformation&amp; 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&amp; encryptedOutput, CryptoPP::StreamTransformation&amp; 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-&gt;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-&gt;MessageEnd();
          // m_StreamTransformationFilter-&gt;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-&gt;MaxRetrievable() == 0) {
          m_Source-&gt;Pump(1024);
      }
  
      // Retrieve the next byte
      byte nextByte;
      size_t noBytes = m_StreamTransformationFilter-&gt;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&lt;CryptoPP::AES&gt;::Decryption decryptor(key, sizeof(key));
  
      if (ifs) {
          if (ofs) {
              CryptStreamBuffer cryptBuf(ifs, decryptor);
              std::istream decrypt(&amp;cryptBuf);
  
              int c;
              while (EOF != (c = decrypt.get())) {
                  ofs &lt;&lt; (char)c;
              }
              ofs.flush();
          }
          else {
              std::cerr &lt;&lt; "Failed to open file '" &lt;&lt; destFileName &lt;&lt; "'." &lt;&lt; endl;
          }
      }
      else {
          std::cerr &lt;&lt; "Failed to open file '" &lt;&lt; sourceFileName &lt;&lt; "'." &lt;&lt; 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&lt;CryptoPP::AES&gt;::Encryption encryptor(key, sizeof(key));
  
      if (ifs) {
          if (ofs) {
              CryptStreamBuffer cryptBuf(ofs, encryptor);
              std::ostream encrypt(&amp;cryptBuf);
  
              int c;
              while (EOF != (c = ifs.get())) {
                  encrypt &lt;&lt; (char)c;
              }
              encrypt.flush();
          }
          else {
              std::cerr &lt;&lt; "Failed to open file '" &lt;&lt; destFileName &lt;&lt; "'." &lt;&lt; endl;
          }
      }
      else {
          std::cerr &lt;&lt; "Failed to open file '" &lt;&lt; sourceFileName &lt;&lt; "'." &lt;&lt; endl;
      }  
  }
  
  /** Decrypt using vanilla crypto++ */
  void DecryptFileVanilla(const char* sourceFileName, const char* destFileName) {
  
      byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
      InitKey(key);
  
      CryptoPP::ECB_Mode&lt;CryptoPP::AES&gt;::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&lt;CryptoPP::AES&gt;::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;
  }
  </code></pre>