Repository: randombit/botan
Pull request #2707 (2021-04-08 14:30:54)

Add derive_key variant supporting AD and secret key


Patch:
diff --git a/doc/api_ref/pbkdf.rst b/doc/api_ref/pbkdf.rst
index 137ad2d3c..5e3b5f4bd 100644
--- a/doc/api_ref/pbkdf.rst
+++ b/doc/api_ref/pbkdf.rst
@@ -28,6 +28,16 @@ specified with all parameters (say "Scrypt" with ``N`` = 8192, ``r`` = 64, and
 
       Derive a key from the specified password and salt, placing it into output.
 
+   .. cpp:function:: void derive_key(uint8_t out[], size_t out_len, \
+                     const char* password, const size_t password_len, \
+                     const uint8_t salt[], size_t salt_len, \
+                     const uint8_t ad[], size_t ad_len, \
+                     const uint8_t key[], size_t key_len) const
+
+      Derive a key from the specified password, salt, associated data, and
+      secret key, placing it into output. The ad and key are both allowed
+      to be empty. Currently non-empty AD/key is only supported with Argon2.
+
    .. cpp:function:: std::string to_string() const
 
       Return a descriptive string including the parameters (iteration count, etc)
diff --git a/src/lib/pbkdf/argon2/argon2.h b/src/lib/pbkdf/argon2/argon2.h
index 82880031a..44447360b 100644
--- a/src/lib/pbkdf/argon2/argon2.h
+++ b/src/lib/pbkdf/argon2/argon2.h
@@ -37,6 +37,12 @@ class BOTAN_PUBLIC_API(2,11) Argon2 final : public PasswordHash
                       const char* password, size_t password_len,
                       const uint8_t salt[], size_t salt_len) const override;
 
+      void derive_key(uint8_t out[], size_t out_len,
+                      const char* password, size_t password_len,
+                      const uint8_t salt[], size_t salt_len,
+                      const uint8_t ad[], size_t ad_len,
+                      const uint8_t key[], size_t key_len) const override;
+
       std::string to_string() const override;
 
       size_t M() const { return m_M; }
diff --git a/src/lib/pbkdf/argon2/argon2pwhash.cpp b/src/lib/pbkdf/argon2/argon2pwhash.cpp
index 6df23127c..a9d1994c3 100644
--- a/src/lib/pbkdf/argon2/argon2pwhash.cpp
+++ b/src/lib/pbkdf/argon2/argon2pwhash.cpp
@@ -30,6 +30,20 @@ void Argon2::derive_key(uint8_t output[], size_t output_len,
           m_family, m_p, m_M, m_t);
    }
 
+void Argon2::derive_key(uint8_t output[], size_t output_len,
+                        const char* password, size_t password_len,
+                        const uint8_t salt[], size_t salt_len,
+                        const uint8_t ad[], size_t ad_len,
+                        const uint8_t key[], size_t key_len) const
+   {
+   argon2(output, output_len,
+          password, password_len,
+          salt, salt_len,
+          key, key_len,
+          ad, ad_len,
+          m_family, m_p, m_M, m_t);
+   }
+
 namespace {
 
 std::string argon2_family_name(uint8_t f)
diff --git a/src/lib/pbkdf/pwdhash.cpp b/src/lib/pbkdf/pwdhash.cpp
index 55145ce50..27ec699bb 100644
--- a/src/lib/pbkdf/pwdhash.cpp
+++ b/src/lib/pbkdf/pwdhash.cpp
@@ -30,6 +30,22 @@
 
 namespace Botan {
 
+void PasswordHash::derive_key(uint8_t out[], size_t out_len,
+                              const char* password, size_t password_len,
+                              const uint8_t salt[], size_t salt_len,
+                              const uint8_t ad[], size_t ad_len,
+                              const uint8_t key[], size_t key_len) const
+   {
+   BOTAN_UNUSED(ad, key);
+
+   if(ad_len == 0 && key_len == 0)
+      return this->derive_key(out, out_len,
+                              password, password_len,
+                              salt, salt_len);
+   else
+      throw Not_Implemented("PasswordHash " + this->to_string() + " does not support AD or key");
+   }
+
 std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec,
                                      const std::string& provider)
    {
diff --git a/src/lib/pbkdf/pwdhash.h b/src/lib/pbkdf/pwdhash.h
index d57775e73..775e8b79c 100644
--- a/src/lib/pbkdf/pwdhash.h
+++ b/src/lib/pbkdf/pwdhash.h
@@ -74,6 +74,32 @@ class BOTAN_PUBLIC_API(2,8) PasswordHash
       virtual void derive_key(uint8_t out[], size_t out_len,
                               const char* password, size_t password_len,
                               const uint8_t salt[], size_t salt_len) const = 0;
+
+      /**
+      * Derive a key from a password plus additional data and/or a secret key
+      *
+      * Currently this is only supported for Argon2. Using a non-empty AD or key
+      * with other algorithms will cause a Not_Implemented exception.
+      *
+      * @param out buffer to store the derived key, must be of out_len bytes
+      * @param out_len the desired length of the key to produce
+      * @param password the password to derive the key from
+      * @param password_len the length of password in bytes
+      * @param salt a randomly chosen salt
+      * @param salt_len length of salt in bytes
+      * @param ad some additional data
+      * @param ad_len length of ad in bytes
+      * @param key a secret key
+      * @param key_len length of key in bytes
+      *
+      * This function is const, but is not thread safe. Different threads should
+      * either use unique objects, or serialize all access.
+      */
+      virtual void derive_key(uint8_t out[], size_t out_len,
+                              const char* password, size_t password_len,
+                              const uint8_t salt[], size_t salt_len,
+                              const uint8_t ad[], size_t ad_len,
+                              const uint8_t key[], size_t key_len) const;
    };
 
 class BOTAN_PUBLIC_API(2,8) PasswordHashFamily


Test patch:
diff --git a/src/tests/test_pbkdf.cpp b/src/tests/test_pbkdf.cpp
index 28f656617..ecdf2afe7 100644
--- a/src/tests/test_pbkdf.cpp
+++ b/src/tests/test_pbkdf.cpp
@@ -20,10 +20,6 @@
    #include <botan/scrypt.h>
 #endif
 
-#if defined(BOTAN_HAS_ARGON2)
-   #include <botan/argon2.h>
-#endif
-
 #if defined(BOTAN_HAS_PBKDF_BCRYPT)
    #include <botan/bcrypt_pbkdf.h>
 #endif
@@ -268,29 +264,8 @@ class Argon2_KAT_Tests final : public Text_Based_Test
          const std::vector<uint8_t> passphrase = vars.get_req_bin("Passphrase");
          const std::vector<uint8_t> expected = vars.get_req_bin("Output");
 
-         uint8_t family;
-         if(mode == "Argon2d")
-            family = 0;
-         else if(mode == "Argon2i")
-            family = 1;
-         else if(mode == "Argon2id")
-            family = 2;
-         else
-            throw Test_Error("Unknown Argon2 mode");
-
          Test::Result result(mode);
 
-         std::vector<uint8_t> output(expected.size());
-         Botan::argon2(output.data(), output.size(),
-                       reinterpret_cast<const char*>(passphrase.data()),
-                       passphrase.size(),
-                       salt.data(), salt.size(),
-                       key.data(), key.size(),
-                       ad.data(), ad.size(),
-                       family, P, M, T);
-
-         result.test_eq("derived key", output, expected);
-
          auto pwdhash_fam = Botan::PasswordHashFamily::create(mode);
 
          if(!pwdhash_fam)
@@ -299,18 +274,17 @@ class Argon2_KAT_Tests final : public Text_Based_Test
             return result;
             }
 
-         if(ad.size() == 0)
-            {
-            auto pwdhash = pwdhash_fam->from_params(M, T, P);
+         auto pwdhash = pwdhash_fam->from_params(M, T, P);
 
-            std::vector<uint8_t> pwdhash_derived(expected.size());
-            pwdhash->derive_key(pwdhash_derived.data(), pwdhash_derived.size(),
-                                reinterpret_cast<const char*>(passphrase.data()),
-                                passphrase.size(),
-                                salt.data(), salt.size());
+         std::vector<uint8_t> pwdhash_derived(expected.size());
+         pwdhash->derive_key(pwdhash_derived.data(), pwdhash_derived.size(),
+                             reinterpret_cast<const char*>(passphrase.data()),
+                             passphrase.size(),
+                             salt.data(), salt.size(),
+                             ad.data(), ad.size(),
+                             key.data(), key.size());
 
-            result.test_eq("pwdhash derived key", pwdhash_derived, expected);
-            }
+         result.test_eq("pwdhash derived key", pwdhash_derived, expected);
 
          return result;
          }