Repository: randombit/botan (cpp)

Issue:
ECIES_Encryptor::encrypt() throws
Just trying out ECIES:

```
#include <botan/auto_rng.h>
#include <botan/ecies.h>
#include <botan/ecdh.h>
#include <iostream>

int main()
{
    try
    {
        Botan::AutoSeeded_RNG rng;
        Botan::ECDH_PrivateKey private_key(rng, Botan::EC_Group("secp521r1"));
        Botan::ECDH_PrivateKey private_key2(rng, Botan::EC_Group("secp521r1"));
        Botan::ECIES_System_Params params(private_key.domain(), "KDF1-18033(SHA-512)",
                                          "AES-256/CBC", 32, "HMAC(SHA-512)", 20,
                                          Botan::PointGFp::Compression_Type::COMPRESSED, Botan::ECIES_Flags::CHECK_MODE);
        Botan::ECIES_Encryptor enc(private_key, params, rng);
        enc.set_other_key(private_key2.public_point());
        std::string plain("some random data");
        std::vector<uint8_t> data(plain.data(), plain.data() + plain.size());
        auto ret = enc.encrypt(data, rng);
    }
    catch (const Botan::Invalid_State &e)
    {
        std::cout << e.what() << std::endl;
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
}
```

output:
`Invalid state: state().empty() == false was false in finish:src/lib/modes/cbc/cbc.cpp`

tried with 2.11.0 and 2.12.0 releases, results are the same.
not sure if i did something wrong? i dont find ECIES example in the docs, so i tried to grab code from [test_ecies.cpp](https://github.com/randombit/botan/blob/master/src/tests/test_ecies.cpp)

it still throws even if i change dem_algo_spec to other unauthenticated cipher modes, so i think its an issue in the `ECIES_Encryptor` implementation?

Pull request:
Fix ECIES and CCM bugs
#2150

Patch:
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp
index effe3a1ff..7a82f1a47 100644
--- a/src/lib/modes/aead/ccm/ccm.cpp
+++ b/src/lib/modes/aead/ccm/ccm.cpp
@@ -136,6 +136,8 @@ void CCM_Mode::inc(secure_vector<uint8_t>& C)
 
 secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz)
    {
+   if(m_nonce.size() != 15-L())
+      throw Invalid_State("CCM mode must set nonce");
    secure_vector<uint8_t> B0(CCM_BS);
 
    const uint8_t b_flags =
@@ -150,6 +152,8 @@ secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz)
 
 secure_vector<uint8_t> CCM_Mode::format_c0()
    {
+   if(m_nonce.size() != 15-L())
+      throw Invalid_State("CCM mode must set nonce");
    secure_vector<uint8_t> C(CCM_BS);
 
    const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
@@ -209,6 +213,8 @@ void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
    T ^= S0;
 
    buffer += std::make_pair(T.data(), tag_size());
+
+   reset();
    }
 
 void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
@@ -266,6 +272,8 @@ void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
       throw Invalid_Authentication_Tag("CCM tag check failed");
 
    buffer.resize(buffer.size() - tag_size());
+
+   reset();
    }
 
 }
diff --git a/src/lib/pubkey/dlies/dlies.cpp b/src/lib/pubkey/dlies/dlies.cpp
index d24542d0e..4aee3ffb3 100644
--- a/src/lib/pubkey/dlies/dlies.cpp
+++ b/src/lib/pubkey/dlies/dlies.cpp
@@ -69,11 +69,9 @@ std::vector<uint8_t> DLIES_Encryptor::enc(const uint8_t in[], size_t length,
       SymmetricKey enc_key(secret_keys.data(), cipher_key_len);
       m_cipher->set_key(enc_key);
 
-      if(m_iv.size())
-         {
-         m_cipher->start(m_iv.bits_of());
-         }
-
+      if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+         throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+      m_cipher->start(m_iv.bits_of());
       m_cipher->finish(ciphertext);
       }
    else
@@ -194,11 +192,9 @@ secure_vector<uint8_t> DLIES_Decryptor::do_decrypt(uint8_t& valid_mask,
             // the decryption can fail:
             // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
 
-            if(m_iv.size())
-               {
-               m_cipher->start(m_iv.bits_of());
-               }
-
+            if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+               throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+            m_cipher->start(m_iv.bits_of());
             m_cipher->finish(ciphertext);
             }
          catch(...)
diff --git a/src/lib/pubkey/ecies/ecies.cpp b/src/lib/pubkey/ecies/ecies.cpp
index 1c8ca45b6..a8c277b3a 100644
--- a/src/lib/pubkey/ecies/ecies.cpp
+++ b/src/lib/pubkey/ecies/ecies.cpp
@@ -287,10 +287,11 @@ std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[], size_t length, R
    // encryption
 
    m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
-   if(m_iv.size() != 0)
-      {
-      m_cipher->start(m_iv.bits_of());
-      }
+   if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+      throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+
+   m_cipher->start(m_iv.bits_of());
+
    secure_vector<uint8_t> encrypted_data(data, data + length);
    m_cipher->finish(encrypted_data);
 
@@ -391,10 +392,9 @@ secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const ui
       // decrypt data
 
       m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
-      if(m_iv.size() != 0)
-         {
-         m_cipher->start(m_iv.bits_of());
-         }
+      if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+         throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+      m_cipher->start(m_iv.bits_of());
 
       try
          {


Test patch:
diff --git a/src/tests/data/pubkey/ecies.vec b/src/tests/data/pubkey/ecies.vec
index 9b22924c3..61bf1463e 100644
--- a/src/tests/data/pubkey/ecies.vec
+++ b/src/tests/data/pubkey/ecies.vec
@@ -58,7 +58,6 @@ OtherPrivateKey = 1255825134563225934367124570783723166851629196761
 Kdf = KDF2(SHA-1)
 Dem = AES-256/CBC
 DemKeyLen = 32
-Iv = 00000000000000000000000000000000
 Mac = HMAC(SHA-256)
 MacKeyLen = 20
 Format = compressed
@@ -76,7 +75,6 @@ OtherPrivateKey = 1255825134563225934367124570783723166851629196761
 Kdf = KDF1-18033(SHA-1)
 Dem = AES-256/CBC
 DemKeyLen = 32
-Iv = 00000000000000000000000000000000
 Mac = HMAC(SHA-256)
 MacKeyLen = 20
 Format = compressed
diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp
index e8e745487..4aef7b744 100644
--- a/src/tests/test_aead.cpp
+++ b/src/tests/test_aead.cpp
@@ -61,6 +61,8 @@ class AEAD_Tests final : public Text_Based_Test
             {
             result.test_throws("Cannot process data until nonce is set (enc)",
                                [&]() { enc->update(garbage); });
+            result.test_throws("Cannot process data until nonce is set (enc)",
+                               [&]() { enc->finish(garbage); });
             }
 
          enc->set_ad(mutate_vec(ad));
@@ -222,6 +224,8 @@ class AEAD_Tests final : public Text_Based_Test
             {
             result.test_throws("Cannot process data until nonce is set (dec)",
                                [&]() { dec->update(garbage); });
+            result.test_throws("Cannot process data until nonce is set (dec)",
+                               [&]() { dec->finish(garbage); });
             }
 
          dec->start(mutate_vec(nonce));
diff --git a/src/tests/test_ecies.cpp b/src/tests/test_ecies.cpp
index 534a2719e..97f6d2606 100644
--- a/src/tests/test_ecies.cpp
+++ b/src/tests/test_ecies.cpp
@@ -196,8 +196,9 @@ class ECIES_Tests final : public Text_Based_Test
       ECIES_Tests()
          : Text_Based_Test(
               "pubkey/ecies.vec",
-              "Curve,PrivateKey,OtherPrivateKey,Kdf,Dem,DemKeyLen,Iv,Mac,MacKeyLen,Format,"
-              "CofactorMode,OldCofactorMode,CheckMode,SingleHashMode,Label,Plaintext,Ciphertext") {}
+              "Curve,PrivateKey,OtherPrivateKey,Kdf,Dem,DemKeyLen,Mac,MacKeyLen,Format,"
+              "CofactorMode,OldCofactorMode,CheckMode,SingleHashMode,Label,Plaintext,Ciphertext",
+              "Iv") {}
 
       Test::Result run_one_test(const std::string&, const VarMap& vars) override
          {
@@ -209,7 +210,7 @@ class ECIES_Tests final : public Text_Based_Test
          const std::string kdf = vars.get_req_str("Kdf");
          const std::string dem = vars.get_req_str("Dem");
          const size_t dem_key_len = vars.get_req_sz("DemKeyLen");
-         const std::vector<uint8_t> iv = vars.get_req_bin("Iv");
+         const std::vector<uint8_t> iv = vars.get_opt_bin("Iv");
          const std::string mac = vars.get_req_str("Mac");
          const size_t mac_key_len = vars.get_req_sz("MacKeyLen");
          const Botan::PointGFp::Compression_Type compression_type = get_compression_type(vars.get_req_str("Format"));