[Botan-devel] Caching derived keys

Magnus Therning magnus at therning.org
Tue Feb 17 03:14:21 EST 2009


Jack Lloyd wrote:
> On Fri, Feb 13, 2009 at 10:27:37PM +0000, Magnus Therning wrote:
> 
>> I've been playing with botan, hoping to replace my use of python-crypto
>> in a python app I've written.  I only need very simply encrypt/decrypt
>> and my class currently looks like this:
>>
>>   class Crypto {
>>       public:
>>           Crypto(std::string pwd);
>>           ~Crypto();
>>
>>           boost::python::tuple encrypt(std::string pt);
>>           std::string decrypt(std::string salt, std::string ct);
>>   };
>>
>> At first I attempted to cache the derived key, but ran into
>> memory/instance ownership issues.  The botan API seems to like passing
>> ownership with arguments and to clean up on method exit.  That made
>> caching difficult so I now derive the key on each call to
>> encrypt/decrypt.  Is there something obvious that I've missed, is it
>> actually really simple to "pre-derive" the key?
> 
> This would depend somewhat on what exactly you are doing inside this
> class, but I'm guessing based on the constructor that you are doing
> some sort of password-based encryption, for example hashing the
> password with PBKDF2 to generate the key.
> 
> So there are actually multiple levels of caching possible here:
> 
> 1) When you call derive_key, you can copy the returned value to a
>    member of your Crypto class. This avoids having to go through the
>    (intentionally) slow PBKDF2 operation each time. This key can be
>    saved/copied around/etc without much complication.
> 
> 2) I assume you are setting up a pipe object with some set of filters
>    for handling encryption/MACing, etc. This object could also be
>    cached. This would avoid repeated memory allocations, plus
>    rerunning the key schedule (which depending on the algorihthm could
>    make a big difference). However doing so woul prevent copying since
>    Pipe cannot be copied (but if you needed to copy your crypto object
>    for some reason you could simply cache both the Pipe and the
>    password-derived key, and writing a copy constructor and assignment
>    operator for Crypto that initialize a new set of filters using the
>    key.
> 
> Hope this helps. It would be easier to provide specific advice if you
> could send the source for your class.

This is the encrypt(std::string) method:

  boost::python::tuple Crypto::encrypt(std::string pt)
  {
      Botan::AutoSeeded_RNG rng;
      std::auto_ptr<Botan::S2K> s2k(Botan::get_s2k("PBKDF2(SHA-1)"));
      s2k->set_iterations(4096);
      s2k->new_random_salt(rng, 8);
      Botan::SecureVector<Botan::byte> the_salt = s2k->current_salt();

      Botan::SecureVector<Botan::byte> master_key = s2k->derive_key(48,
passphrase).bits_of();

      std::auto_ptr<Botan::KDF> kdf(Botan::get_kdf("KDF2(SHA-1)"));

      Botan::SymmetricKey key = kdf->derive_key(32, master_key, "cipher
key");
      Botan::InitializationVector iv = kdf->derive_key(16, master_key,
"cipher iv");

      Botan::Pipe pipe(get_cipher("AES/CBC/PKCS7", key, iv,
Botan::ENCRYPTION));
      pipe.process_msg(pt);

      std::string salt_string((const char *)the_salt.begin(),
the_salt.size());
      std::string ct_string(pipe.read_all_as_string());
      return boost::python::make_tuple(salt_string, ct_string);
  }

And here's decrypt(std::string, std::string):

  std::string Crypto::decrypt(std::string salt, std::string ct)
  {
      std::auto_ptr<Botan::S2K> s2k(Botan::get_s2k("PBKDF2(SHA-1)"));
      s2k->set_iterations(4096);
      s2k->change_salt((const Botan::byte *)salt.c_str(), salt.size());

      Botan::SecureVector<Botan::byte> master_key = s2k->derive_key(48,
passphrase).bits_of();

      std::auto_ptr<Botan::KDF> kdf(Botan::get_kdf("KDF2(SHA-1)"));

      Botan::SymmetricKey key = kdf->derive_key(32, master_key, "cipher
key");
      Botan::InitializationVector iv = kdf->derive_key(16, master_key,
"cipher iv");

      Botan::Pipe pipe(get_cipher("AES/CBC/PKCS7", key, iv,
Botan::DECRYPTION));
      pipe.process_msg(ct);

      std::string pt_string(pipe.read_all_as_string());
      return pt_string;
  }

As you can guess I am now caching the *clear-text password* rather than
the derived key/iv.  The naïve approach of caching the
SecureVector<Byte> instances failed on what I presume basically is a
double-free since the Pipe takes ownership.

/M

-- 
Magnus Therning                        (OpenPGP: 0xAB4DFBA4)
magnus@therning.org          Jabber: magnus@therning.org
http://therning.org/magnus         identi.ca|twitter: magthe

Haskell is an even 'redder' pill than Lisp or Scheme.
     -- PaulPotts

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 197 bytes
Desc: OpenPGP digital signature
URL: <http://lists.randombit.net/pipermail/botan-devel/attachments/20090217/d5489ce0/attachment.asc>


More information about the botan-devel mailing list