Repository: randombit/botan
Pull request #2440 (2020-10-31 09:50:18)

Generalize SIV's support for multiple ADs to the AEAD interface


Patch:
diff --git a/src/lib/modes/aead/aead.cpp b/src/lib/modes/aead/aead.cpp
index 077e09e5c..9ad550ffd 100644
--- a/src/lib/modes/aead/aead.cpp
+++ b/src/lib/modes/aead/aead.cpp
@@ -39,6 +39,14 @@
 
 namespace Botan {
 
+void AEAD_Mode::set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len)
+   {
+   if(i == 0)
+      this->set_associated_data(ad, ad_len);
+   else
+      throw Invalid_Argument("AEAD '" + name() + "' does not support multiple associated data");
+   }
+
 std::unique_ptr<AEAD_Mode> AEAD_Mode::create_or_throw(const std::string& algo,
                                                       Cipher_Dir dir,
                                                       const std::string& provider)
diff --git a/src/lib/modes/aead/aead.h b/src/lib/modes/aead/aead.h
index 92957fb24..442eb8ed7 100644
--- a/src/lib/modes/aead/aead.h
+++ b/src/lib/modes/aead/aead.h
@@ -60,6 +60,32 @@ class BOTAN_PUBLIC_API(2,0) AEAD_Mode : public Cipher_Mode
       */
       virtual void set_associated_data(const uint8_t ad[], size_t ad_len) = 0;
 
+      /**
+      * Set associated data that is not included in the ciphertext but
+      * that should be authenticated. Must be called after set_key and
+      * before start.
+      *
+      * Unless reset by another call, the associated data is kept
+      * between messages. Thus, if the AD does not change, calling
+      * once (after set_key) is the optimum.
+      *
+      * Some AEADs (namely SIV) support multiple AD inputs. For
+      * all other modes only nominal AD input 0 is supported; all
+      * other values of i will cause an exception.
+      *
+      * @param ad the associated data
+      * @param ad_len length of add in bytes
+      */
+      virtual void set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len);
+
+      /**
+      * Returns the maximum supported number of associated data inputs which
+      * can be provided to set_associated_data_n
+      *
+      * If returns 0, then no associated data is supported.
+      */
+      virtual size_t maximum_associated_data_inputs() const { return 1; }
+
       /**
       * Most AEADs require the key to be set prior to setting the AD
       * A few allow the AD to be set even before the cipher is keyed.
diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp
index 75bdb91c4..8bd82a468 100644
--- a/src/lib/modes/aead/siv/siv.cpp
+++ b/src/lib/modes/aead/siv/siv.cpp
@@ -78,9 +78,14 @@ void SIV_Mode::key_schedule(const uint8_t key[], size_t length)
    m_ad_macs.clear();
    }
 
+size_t SIV_Mode::maximum_associated_data_inputs() const
+   {
+   return block_size() * 8 - 2;
+   }
+
 void SIV_Mode::set_associated_data_n(size_t n, const uint8_t ad[], size_t length)
    {
-   const size_t max_ads = block_size() * 8 - 2;
+   const size_t max_ads = maximum_associated_data_inputs();
    if(n > max_ads)
       throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
 
diff --git a/src/lib/modes/aead/siv/siv.h b/src/lib/modes/aead/siv/siv.h
index 44e0a6bf9..c76fd3229 100644
--- a/src/lib/modes/aead/siv/siv.h
+++ b/src/lib/modes/aead/siv/siv.h
@@ -33,7 +33,9 @@ class BOTAN_PUBLIC_API(2,0) SIV_Mode : public AEAD_Mode
       * @param ad associated data
       * @param ad_len length of associated data in bytes
       */
-      void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len);
+      void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len) override;
+
+      size_t maximum_associated_data_inputs() const override;
 
       void set_associated_data(const uint8_t ad[], size_t ad_len) override
          {


Test patch:
diff --git a/src/tests/test_siv.cpp b/src/tests/test_siv.cpp
index 886b817a6..83a936543 100644
--- a/src/tests/test_siv.cpp
+++ b/src/tests/test_siv.cpp
@@ -7,8 +7,7 @@
 #include "tests.h"
 
 #if defined(BOTAN_HAS_AEAD_SIV)
-   #include <botan/siv.h>
-   #include <botan/block_cipher.h>
+   #include <botan/aead.h>
    #include <botan/parsing.h>
 #endif
 
@@ -32,18 +31,18 @@ class SIV_Tests final : public Text_Based_Test
          const std::vector<std::string> ad_list =
             Botan::split_on(vars.get_req_str("ADs"), ',');
 
-         Test::Result result(algo + "/SIV");
+         const std::string siv_name = algo + "/SIV";
 
-         auto cipher = Botan::BlockCipher::create(algo);
+         Test::Result result(siv_name);
 
-         if(!cipher)
+         auto siv = Botan::AEAD_Mode::create(siv_name, Botan::ENCRYPTION);
+
+         if(!siv)
             {
             result.test_note("Skipping test due to missing cipher");
             return result;
             }
 
-         std::unique_ptr<Botan::SIV_Mode> siv(new Botan::SIV_Encryption(cipher.release()));
-
          siv->set_key(key);
 
          for(size_t i = 0; i != ad_list.size(); ++i)