Allows the user to specify what hash and what block cipher to use.
Uses the max keylength by default, but can also specify that template
parameter (assuming you specify a valid keylength for your algorithm
of choice!).

I propose this is reviewed/committed.

Example usage:

    typedef
CryptoPP::PassphrasedDecryptor<CryptoPP::SHA512,CryptoPP::Serpent>
Decryptor;
    typedef
CryptoPP::PassphrasedEncryptor<CryptoPP::SHA512,CryptoPP::Serpent>
Encryptor;
    std::string strIn, strOut;
    strIn = "Hello World!;
    CryptoPP::StringSource( strIn, true,
        new Encryptor("password",
            new Decryptor("password",
                new CryptoPP::StringSink( strOut )
            )
        )
    );
    std::cout << "Out: " << strOut << std::endl;


Code:

#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)

// ********************************************************

// Common base class, used to keep constant definitions in one place,
and add two functions.
template <class HashType,class CipherType>
class PassphrasedCommon
{
    protected:
        PassphrasedCommon() { }
        typedef typename CryptoPP::CBC_Mode<CipherType>::Encryption
EncryptionCipher;
        typedef typename CryptoPP::CBC_Mode<CipherType>::Decryption
DecryptionCipher;
        CRYPTOPP_CONSTANT(BLOCKSIZE =
CipherType::Encryption::BLOCKSIZE);
        CRYPTOPP_CONSTANT(DIGESTSIZE = HashType::DIGESTSIZE)
        // Digest size aligned to a BLOCKSIZE boundary (so, >=
DIGESTSIZE)
        CRYPTOPP_CONSTANT(ENC_DIGESTSIZE = (BLOCKSIZE*((DIGESTSIZE
+BLOCKSIZE-1)/BLOCKSIZE)) )
        // Amount of padding required to make DIGESTSIZE fill
ENC_DIGESTSIZE
        CRYPTOPP_CONSTANT(DIGEST_PAD = (ENC_DIGESTSIZE-DIGESTSIZE))
        // Amount of data to frontload for decryption
        CRYPTOPP_CONSTANT(HEADER_SIZE = (2*ENC_DIGESTSIZE) );
        // Number of iterations to run through the key derivation
function
        CRYPTOPP_CONSTANT(KEY_ITERATIONS = 2000 );

        // Put out the proper amount of badding
        size_t PutPad(BufferedTransformation*transformation)
        {
            size_t ret=0;
            if (transformation)
                for (size_t count=DIGEST_PAD;count;--count)
                    ret += transformation->Put('\0');
            return ret;
        }
        // Skip the proper amount of padding
        size_t SkipPad(BufferedTransformation*transformation)
        {
            if (!transformation)
                return 0;
            return transformation->Skip(DIGEST_PAD);
        }

};

// ********************************************************

// Encryption filter
template <class HashType,class CipherType,const size_t
KEY_SIZE=CipherType::MAX_KEYLENGTH>
class PassphrasedEncryptor : public ProxyFilter, public
PassphrasedCommon<HashType,CipherType>
{
    private:
        // Pull in base values
        typedef PassphrasedCommon<HashType,CipherType> Base;
        CRYPTOPP_CONSTANT(BLOCKSIZE = Base::BLOCKSIZE);
        CRYPTOPP_CONSTANT(DIGESTSIZE = Base::DIGESTSIZE)
        CRYPTOPP_CONSTANT(ENC_DIGESTSIZE = Base::ENC_DIGESTSIZE );
        CRYPTOPP_CONSTANT(KEY_ITERATIONS = Base::KEY_ITERATIONS );
        typename Base::EncryptionCipher m_cipher;

        SecByteBlock m_passphrase;


    public:
        PassphrasedEncryptor(const char *passphrase,
BufferedTransformation *attachment = NULL)
        : ProxyFilter(NULL,0,0,attachment), m_passphrase((const byte *)
passphrase, strlen(passphrase))
        {

        }
        PassphrasedEncryptor(const byte *passphrase, size_t
passphraseLength, BufferedTransformation *attachment = NULL)
        : ProxyFilter(NULL,0,0,attachment), m_passphrase(passphrase,
passphraseLength)
        {

        }

    protected:
        void FirstPut(const byte *inString)
        {
            SecByteBlock salt(DIGESTSIZE), keyCheck(DIGESTSIZE);

            HashType hash;

            // 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
            AttachedTransformation()->Put(salt, salt.size());
            // padd it to an even BLOCKSIZE boundary
            Base::PutPad(AttachedTransformation());

            // Buffer with room for the key and the initial value
            SecByteBlock keyIV(KEY_SIZE+BLOCKSIZE);
            // Derive the key
            PKCS5_PBKDF2_HMAC<HashType>().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,KEY_SIZE,keyIV+KEY_SIZE);

            // Set the cipher as the filter
            SetFilter(new StreamTransformationFilter(m_cipher));
            // Output the keyCheck value before all others
            m_filter->Put(keyCheck, keyCheck.size());
            // Pad it to an even BLOCKSIZE boundary
            Base::PutPad(m_filter.get());
        }
        void LastPut(const byte *inString, size_t length)
        {
            m_filter->MessageEnd();
        }
};

// ********************************************************

// Decryption filter
template <class HashType,class CipherType,const size_t
KEY_SIZE=CipherType::MAX_KEYLENGTH>
class PassphrasedDecryptor : public ProxyFilter, public
PassphrasedCommon<HashType,CipherType>
{
    private:
        typedef PassphrasedCommon<HashType,CipherType> Base;
        typename Base::DecryptionCipher m_cipher;
        CRYPTOPP_CONSTANT(BLOCKSIZE = Base::BLOCKSIZE);
        CRYPTOPP_CONSTANT(DIGESTSIZE = Base::DIGESTSIZE)
        CRYPTOPP_CONSTANT(ENC_DIGESTSIZE = Base::ENC_DIGESTSIZE );
        CRYPTOPP_CONSTANT(HEADER_SIZE = Base::HEADER_SIZE );
        CRYPTOPP_CONSTANT(KEY_ITERATIONS = Base::KEY_ITERATIONS );

        SecByteBlock m_passphrase;
        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:
        PassphrasedDecryptor(const char *passphrase,
BufferedTransformation *attachment = NULL, bool throwException=true)
        : ProxyFilter(NULL, HEADER_SIZE, 0, attachment)
        , m_passphrase((const byte *)passphrase, strlen(passphrase))
        , m_throwException(throwException)
        , m_state(WAITING_FOR_KEYCHECK)
        {

        }

        PassphrasedDecryptor(const byte *passphrase, size_t
passphraseLength, BufferedTransformation *attachment = NULL, bool
throwException=true)
        : ProxyFilter(NULL, HEADER_SIZE, 0, attachment)
        , m_passphrase(passphrase, passphraseLength)
        , m_throwException(throwException)
        , m_state(WAITING_FOR_KEYCHECK)
        {

        }

        State CurrentState() const {return m_state;}

    protected:
        void FirstPut(const byte *inString)
        {
            CheckKey(inString, inString+ENC_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)
        {
            //(STDMAX((unsigned int)2*BLOCKSIZE, (unsigned int)
DefaultHashModule::DIGESTSIZE));
            SecByteBlock calcCheck(DIGESTSIZE);
            SecByteBlock readCheck(DIGESTSIZE);

            // Calculate the expected keyCheck value using the salt
from the input stream
            HashType hash;
            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
            SecByteBlock keyIV(KEY_SIZE+BLOCKSIZE);
            CryptoPP::PKCS5_PBKDF2_HMAC<HashType>().DeriveKey
(keyIV,keyIV.size(),0,m_passphrase,m_passphrase.size
(),salt,DIGESTSIZE,KEY_ITERATIONS);
            m_cipher.SetKeyWithIV(keyIV,KEY_SIZE,keyIV+KEY_SIZE);

            // 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, ENC_DIGESTSIZE);
            // Force it to be processed now.
            decryptor->ForceNextPut();
            // Read back the decrypted keyCheck value.
            decryptor->Get(readCheck, readCheck.size());
            // Skip over the extra padding
            Base::SkipPad(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