Personally, I would create some kind of abstract interface representing an encryption algorithm, with a factory function taking the key and producing a concrete instance of an encryption algorithm with a key installed. So the 'second class' here would call directly to the 'first class', but there would be a 'third class' in charge of instantiating the class. Something like:
/* Core encryption framework definitions */
class CipherInstance {
// ...
public:
virtual void encrypt(void *, size_t) = 0;
virtual void decrypt(void *, size_t) = 0;
virtual size_t blocksize() const = 0;
// ...
virtual ~CipherInstance() { }
};
class Cipher {
public:
virtual CipherInstance *instantiate(const void *key, size_t keylen) = 0;
virtual size_t minkeysize() const = 0;
virtual size_t maxkeysize() const = 0;
};
/* AES implementation */
class privateAESImpl : public Cipher { /* ... */ };
// This is the only public definition in the AES implementation. The privateAESImpl
// class is a stateless singleton, and this is the only instance. Doing this instead
// of static functions allows AES to be passed to a function taking a Cipher *
extern privateAESImpl AES;
// Much later:
CipherInstance *aes = AES.instantiate(key, keylen);
aes->encrypt(data, datalen);
// or, to be more general:
void frob(Cipher *cipher, void *key, size_t keylen, void *data, size_t datalen) {
CipherInstance *inst = cipher->instantiate(key, keylen);
inst->encrypt(data, datalen);
}
C#'s System.Security.Cryptography
libraries use a similar approach - see, eg, System.Security.Cryptography.SymmetricAlgorithm
. Note however that since C# supports introspection, there's no need for a factory class - instead there's simply a static method taking a name. With C++ a full factory class is needed.