Repository: randombit/botan
Pull request #3172 (2023-01-13 13:24:48)

Add Cipher_Mode::requires_entire_message
Previously update_granularity == 1 was an (undocumented and not reliable) method of detecting if an AEAD mode required presenting the entire message before processing could occur.  Since #3168 this assumption is no longer valid.

Add a method which directly indicates if the cipher mode has this nature.

Inspired by #3171 as logic along the lines above may (possibly) be the root cause of the problem.


Patch:
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp
index 7892c1e88..8ebd31c32 100644
--- a/src/lib/modes/aead/ccm/ccm.cpp
+++ b/src/lib/modes/aead/ccm/ccm.cpp
@@ -71,6 +71,11 @@ size_t CCM_Mode::ideal_granularity() const
    return m_cipher->parallel_bytes();
    }
 
+bool CCM_Mode::requires_entire_message() const
+   {
+   return true;
+   }
+
 Key_Length_Specification CCM_Mode::key_spec() const
    {
    return m_cipher->key_spec();
diff --git a/src/lib/modes/aead/ccm/ccm.h b/src/lib/modes/aead/ccm/ccm.h
index 18de03f49..9bbc1f35e 100644
--- a/src/lib/modes/aead/ccm/ccm.h
+++ b/src/lib/modes/aead/ccm/ccm.h
@@ -33,6 +33,8 @@ class CCM_Mode : public AEAD_Mode
 
       size_t ideal_granularity() const override;
 
+      bool requires_entire_message() const override;
+
       Key_Length_Specification key_spec() const override;
 
       bool valid_nonce_length(size_t) const override;
diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp
index f3368acf9..68ffde1dd 100644
--- a/src/lib/modes/aead/siv/siv.cpp
+++ b/src/lib/modes/aead/siv/siv.cpp
@@ -65,6 +65,11 @@ size_t SIV_Mode::ideal_granularity() const
    return 128;
    }
 
+bool SIV_Mode::requires_entire_message() const
+   {
+   return true;
+   }
+
 Key_Length_Specification SIV_Mode::key_spec() const
    {
    return m_mac->key_spec().multiple(2);
diff --git a/src/lib/modes/aead/siv/siv.h b/src/lib/modes/aead/siv/siv.h
index 3048f7cc9..2d5f23d7f 100644
--- a/src/lib/modes/aead/siv/siv.h
+++ b/src/lib/modes/aead/siv/siv.h
@@ -50,6 +50,8 @@ class BOTAN_TEST_API SIV_Mode : public AEAD_Mode
 
       bool valid_nonce_length(size_t) const override;
 
+      bool requires_entire_message() const override;
+
       void clear() override;
 
       void reset() override;
diff --git a/src/lib/modes/cipher_mode.h b/src/lib/modes/cipher_mode.h
index af231f7d5..a401cb997 100644
--- a/src/lib/modes/cipher_mode.h
+++ b/src/lib/modes/cipher_mode.h
@@ -156,6 +156,17 @@ class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
       */
       virtual size_t ideal_granularity() const = 0;
 
+      /**
+      * Certain modes require the entire message be available before
+      * any processing can occur. For such modes, input will be consumed
+      * but not returned, until `finish` is called, which returns the
+      * entire message.
+      *
+      * This function returns true if this mode has this style of
+      * operation.
+      */
+      virtual bool requires_entire_message() const { return false; }
+
       /**
       * @return required minimium size to finalize() - may be any
       *         length larger than this.


Test patch:
diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp
index afd0591d7..271b84521 100644
--- a/src/tests/test_aead.cpp
+++ b/src/tests/test_aead.cpp
@@ -159,6 +159,10 @@ class AEAD_Tests final : public Text_Based_Test
 
                const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
 
+               result.confirm("Process returns data unless requires_entire_message",
+                              enc->requires_entire_message(),
+                              bytes_written == 0);
+
                if(bytes_written == 0)
                   {
                   // SIV case
@@ -315,6 +319,10 @@ class AEAD_Tests final : public Text_Based_Test
 
                const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
 
+               result.confirm("Process returns data unless requires_entire_message",
+                              dec->requires_entire_message(),
+                              bytes_written == 0);
+
                if(bytes_written == 0)
                   {
                   // SIV case