I began tweaking my implementation of a password-based encryption
class similar to the ones in default.h/cpp.

I've gotten it where I can use any block cipher(in a mode) or stream
cipher I wish... except for panama.

If you don't use the gzip filter, there's no crash with panama.. it
just outputs a file that is _not_ the original.  With gzip, it detects
data corruption and throws an error... making the problem easier to
notice.

Essentially, what passcrypt does is it creates a salt, key, and iv.
It outputs the salt to the file (aligned to a blocksize boundary),
then using the encryption specified outputs a "keycheck" value that
can be used to validate that you have entered the proper password,
then the rest of the file.

Decryption reads in the salt, creates the expected keycheck, then
reads/decrypts the keycheck from the file and compares them.  Well,
even with panama up to this point it works fine.  The key is verified
to be correct.. but at some point later in the file the corruption is
detected.  The test file that induces the crash for me is available at
http://www.ubercpp.com/test_file.zip  (it's just the .exe file for
"PStart"... my test driver program just encrypts that file (and
crashes with panama).. you dont need to run it).

So, my question is... why does this work with AES, Serpent, BLOWFISH,
Sosemanuk, Salsa20, ... but not panama?  Any help here would be
greatly appreciated, as it's quite bothersome that all but one
algorithm work for me.

/***************** DRIVER **********************/
#include "passcrypt.h"
#include <cryptopp/sha.h>
#include <cryptopp/files.h>
#include <cryptopp/gzip.h>
#include <cryptopp/panama.h>
#include <cryptopp/serpent.h>
#include <cryptopp/sosemanuk.h>
#include <cryptopp/salsa.h>
#include <cryptopp/blowfish.h>

int main(int argc,char*argv[])
{
    typedef CryptoPP::SHA HashType;
    //typedef CryptoPP::CBC_Mode<CryptoPP::Serpent> CryptType;  //
WORKS
    //typedef CryptoPP::CBC_Mode<CryptoPP::Blowfish> CryptType; //
WORKS
    typedef CryptoPP::PanamaCipher<> CryptType;               //
CRASH: Passes keycheck for decryption, but then gzip detects
corruption and throws
    //typedef CryptoPP::Sosemanuk CryptType;                    //
WORKS
    //typedef CryptoPP::Salsa20 CryptType;                      //
WORKS

    CryptoPP::FileSource( "PStart.exe", true,
        new CryptoPP::Gzip(
            new CryptoPP::PassphrasedEncrypt<HashType,CryptType>
((byte*)"password",8,
                    new CryptoPP::FileSink(
                        "PStart.exe.crypt"
                    )
            ),
            CryptoPP::Gzip::MAX_DEFLATE_LEVEL
        )
    );

    CryptoPP::FileSource( "PStart.exe.crypt", true,
        new CryptoPP::PassphrasedDecrypt<HashType,CryptType>
((byte*)"password",8,
            new CryptoPP::Gunzip(
                new CryptoPP::FileSink(
                        "PStart.decrypt.exe"
                )
            )
        )
    );
    return 0;
}

/***************** HEADER (passcrypt.h) **********************/
#ifndef PASSCRYPT_H_INCLUDED
#define PASSCRYPT_H_INCLUDED
#include <cryptopp/rsa.h> // required or pwdbased errors
#include <cryptopp/pwdbased.h>
#include <cryptopp/filters.h>
#include <cryptopp/modes.h>
#include <ctime>


NAMESPACE_BEGIN(CryptoPP)

template <class HashType,class CryptType>
class PassphrasedBase
{
    protected:
        typedef PKCS5_PBKDF2_HMAC<HashType> PBKDF;

        CryptType m_cipher;
        SecByteBlock m_passphrase;

        const size_t DIGESTSIZE;
        const size_t KEYSIZE;
        const size_t IVSIZE;
        const size_t BLOCKSIZE;
        CRYPTOPP_CONSTANT(KEY_ITERATIONS = 2000 );

        PassphrasedBase(const byte *passphrase, size_t
passphraseLength)
        : m_passphrase(passphrase, passphraseLength)
        , DIGESTSIZE(HashType().DigestSize())
        , KEYSIZE(m_cipher.DefaultKeyLength())
        , IVSIZE(m_cipher.IVSize())
        , BLOCKSIZE(m_cipher.MandatoryBlockSize())
        {
          /*  std::cout << "BLOCKSIZE: " << BLOCKSIZE << std::endl;
            std::cout << "KEYSIZE: " << KEYSIZE << std::endl;
            std::cout << "DIGESTSIZE: " << DIGESTSIZE << std::endl;
            std::cout << "ALIGNEDDIGEST: " << BlockAlign(DIGESTSIZE)
<< std::endl;*/
        }

        PassphrasedBase(const char *passphrase)
        : m_passphrase(passphrase, strlen(passphrase))
        , DIGESTSIZE(HashType().DigestSize())
        , KEYSIZE(m_cipher.DefaultKeyLength())
        , IVSIZE(m_cipher.IVSize())
        , BLOCKSIZE(m_cipher.MandatoryBlockSize())
        {
        }

        size_t BlockAlign(const size_t&uSize) const
        {
            return (BLOCKSIZE*((uSize+BLOCKSIZE-1)/BLOCKSIZE));
        }
        void AlignedPut
(SecByteBlock&uBlock,BufferedTransformation*transformation)
        {
            transformation->Put(uBlock,uBlock.size());
            for (size_t count=(BlockAlign(uBlock.size())-uBlock.size
());count;--count)
                transformation->Put('\0');
        }
        void AlignedGet
(SecByteBlock&uBlock,BufferedTransformation*transformation)
        {
            transformation->Get(uBlock,uBlock.size());
            transformation->Skip(BlockAlign(uBlock.size())-uBlock.size
());
        }

};

template <class HashType,class CryptType>
class PassphrasedEncrypt : public PassphrasedBase<HashType,typename
CryptType::Encryption>, public ProxyFilter
{
    private:
        typedef PassphrasedBase<HashType,typename
CryptType::Encryption> Base;
        typedef typename Base::PBKDF PBKDF;
        using Base::m_cipher;
        using Base::m_passphrase;
        using Base::DIGESTSIZE;
        using Base::KEYSIZE;
        using Base::IVSIZE;
        using Base::KEY_ITERATIONS;
        using Base::AlignedPut;
        using Base::AlignedGet;
        using Base::BlockAlign;

    public:
        PassphrasedEncrypt(const byte *passphrase, size_t
passphraseLength, BufferedTransformation *attachment = NULL)
        : Base(passphrase,passphraseLength)
        , ProxyFilter(NULL,0,0,attachment)
        {
            /*std::string strAlgo=ptCipher->AlgorithmName();
            if (strAlgo.size()>=6 &*/
        }

        void FirstPut(const byte *inString)
        {
            HashType hash;
            SecByteBlock salt(DIGESTSIZE), keyCheck(DIGESTSIZE);

            // use hash(passphrase | time | clock) as salt
            hash.Update(m_passphrase, m_passphrase.size());
            std::time_t t=std::time(0);
            hash.Update((byte *)&t, sizeof(t));
            std::clock_t c=std::clock();
            hash.Update((byte *)&c, sizeof(c));
            hash.Final(salt);

            // use hash(passphrase | salt) as key check
            hash.Update(m_passphrase, m_passphrase.size());
            hash.Update(salt, salt.size());
            hash.Final(keyCheck);

            // put the salt into the output stream, unencrypted
            AlignedPut(salt,AttachedTransformation());

            // Buffer with room for the key and the initial value
            SecByteBlock keyIV(KEYSIZE+IVSIZE);
            // Derive the key
            PBKDF().DeriveKey(keyIV,keyIV.size(),
0,m_passphrase,m_passphrase.size(),salt,salt.size(),KEY_ITERATIONS);

            // Prepare the cipher for use given our key and initial
value
            m_cipher.SetKeyWithIV(keyIV,KEYSIZE,keyIV+KEYSIZE);

            // Set the cipher as the filter
            SetFilter(new StreamTransformationFilter(m_cipher));
            // Output the keyCheck value before all others
            AlignedPut(keyCheck,m_filter.get());
        }
        void LastPut(const byte *inString, size_t length)
        {
            m_filter->MessageEnd();
        }
};




template <class HashType,class CryptType>
class PassphrasedDecrypt : public PassphrasedBase<HashType,typename
CryptType::Decryption>,  public ProxyFilter
{
    private:
        typedef PassphrasedBase<HashType,typename
CryptType::Decryption> Base;
        typedef typename Base::PBKDF PBKDF;
        using Base::m_cipher;
        using Base::m_passphrase;
        using Base::DIGESTSIZE;
        using Base::KEYSIZE;
        using Base::IVSIZE;
        using Base::KEY_ITERATIONS;
        using Base::AlignedPut;
        using Base::AlignedGet;
        using Base::BlockAlign;
        bool m_throwException;
    public:
        class Err : public Exception
        {
            public:
            Err(const std::string &s)
            : Exception(DATA_INTEGRITY_CHECK_FAILED, s) {}
        };
        class KeyBadErr : public Err {public: KeyBadErr() : Err
("PassphrasedDecryptor: cannot decrypt message with this passphrase")
{}};
        enum State {WAITING_FOR_KEYCHECK, KEY_GOOD, KEY_BAD};
    protected:
        State m_state;
    public:
        State CurrentState() const {return m_state;}

        PassphrasedDecrypt(const byte *passphrase, size_t
passphraseLength, BufferedTransformation *attachment = NULL, bool
throwException=true)
        : Base(passphrase,passphraseLength)
        , ProxyFilter(NULL,2*BlockAlign(DIGESTSIZE),0,attachment)
        , m_throwException(throwException)
        , m_state(WAITING_FOR_KEYCHECK)
        {
        }
        void FirstPut(const byte *inString)
        {
            CheckKey(inString, inString+BlockAlign(DIGESTSIZE));
        }
        void LastPut(const byte *inString, size_t length)
        {
            if (m_filter.get() == NULL)
            {
                m_state = KEY_BAD;
                if (m_throwException)
                    throw KeyBadErr();
            }
            else
            {
                m_filter->MessageEnd();
                m_state = WAITING_FOR_KEYCHECK;
            }
        }

        void CheckKey(const byte *salt, const byte *encCheck)
        {
            HashType hash;
            SecByteBlock calcCheck(DIGESTSIZE), readCheck(DIGESTSIZE);
            // Calculate the expected keyCheck value using the salt
from the input stream

            hash.Update(m_passphrase, m_passphrase.size());
            hash.Update(salt, DIGESTSIZE);
            hash.Final(calcCheck);

            // Prepare the cipher using the key derived from the input
password and the salt

            // Buffer with room for the key and the initial value
            SecByteBlock keyIV(KEYSIZE+IVSIZE);
            PBKDF().DeriveKey(keyIV,keyIV.size(),
0,m_passphrase,m_passphrase.size(),salt,DIGESTSIZE,KEY_ITERATIONS);
            m_cipher.SetKeyWithIV(keyIV,KEYSIZE,keyIV+KEYSIZE);

            // Make an instance of the decryption cipher
            std::auto_ptr<StreamTransformationFilter> decryptor(new
StreamTransformationFilter(m_cipher));

            // Put the keyCheck read in from the file into the
decryption cipher
            decryptor->Put(encCheck, BlockAlign(DIGESTSIZE));
            // Force it to be processed now.
            decryptor->ForceNextPut();
            // Read back the decrypted keyCheck value.
            AlignedGet(readCheck,decryptor.get());

            // Set the decryption cipher as the filter for this
object.
            SetFilter(decryptor.release());

            // Compare the calculated keyCheck with the one decrypted
from the file
            if (memcmp(calcCheck, readCheck, calcCheck.size()))
            {
                m_state = KEY_BAD;
                if (m_throwException)
                    throw KeyBadErr();
            }
            else
                m_state = KEY_GOOD;

        }

};

NAMESPACE_END

#endif // PASSCRYPT_H_INCLUDED

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the "Crypto++ Users" 
Google Group.
To unsubscribe, send an email to [email protected].
More information about Crypto++ and this group is available at 
http://www.cryptopp.com.
-~----------~----~----~----~------~----~------~--~---

Reply via email to