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.
-~----------~----~----~----~------~----~------~--~---