Workaround for GCM IV length issue 71/293271/3
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 23 May 2023 09:07:23 +0000 (11:07 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 5 Jun 2023 14:05:26 +0000 (16:05 +0200)
GCM implementation was ignoring IV bytes except first 12B. The issue has
been fixed but we need to support the decryption of the data encrypted
the old way.

This workaround retries the decryption with an IV truncated to 12B if
the decryption with original IV length fails.

Unit-test included.

Change-Id: Ia1c06d9a7c6f3b75a69c2e1cb3e5f0801776e057

src/manager/crypto/sw-backend/internals.cpp
unit-tests/test_sw-backend.cpp

index b6aecae..529f4f1 100644 (file)
@@ -823,21 +823,34 @@ RawBuffer decryptDataAesGcm(
        const RawBuffer &tag,
        const RawBuffer &aad)
 {
-       EvpCipherPtr dec;
-       selectCipher(AlgoType::AES_GCM, key.size(), false)(dec, key, iv);
-       void *ptr = (void *)tag.data();
+       RawBuffer result, tmp;
 
-       dec->Control(EVP_CTRL_GCM_SET_TAG, tag.size(), ptr);
+       auto decrypt = [&](const RawBuffer &actualIv){
+               EvpCipherPtr dec;
+               selectCipher(AlgoType::AES_GCM, key.size(), false)(dec, key, actualIv);
+               void *ptr = (void *)tag.data();
 
-       if (!aad.empty())
-               dec->AppendAAD(aad);
+               dec->Control(EVP_CTRL_GCM_SET_TAG, tag.size(), ptr);
+
+               if (!aad.empty())
+                       dec->AppendAAD(aad);
+
+               result = dec->Append(data);
+               try {
+                       tmp = dec->Finalize();
+               } catch (const Exc::Exception &e) {
+                       ThrowErr(Exc::InputParam,
+                                "Tag authentication failed in AES finalize function (the tag doesn't match).");
+               }
+       };
 
-       RawBuffer result = dec->Append(data);
-       RawBuffer tmp;
        try {
-               tmp = dec->Finalize();
-       } catch (const Exc::Exception &e) {
-               ThrowErr(Exc::InputParam, "Tag authentication failed in AES finalize function (the tag doesn't match).");
+               decrypt(iv);
+       } catch (const Exc::InputParam &e) {
+               LogDebug("AES GCM decryption failed. Retry with default iv length.");
+               RawBuffer shortIv = iv;
+               shortIv.resize(Params::DEFAULT_AES_GCM_IV_LEN);
+               decrypt(shortIv);
        }
        std::copy(tmp.begin(), tmp.end(), std::back_inserter(result));
        return result;
index 9fd0264..c064340 100644 (file)
@@ -675,6 +675,44 @@ NEGATIVE_TEST_CASE(symmetricEncryptDecryptGcm)
        BOOST_REQUIRE_THROW(key->decrypt(ca2, encrypted), Exc::Crypto::InputParam);
 }
 
+POSITIVE_TEST_CASE(gcmIvLengthScrewUpWorkaround)
+{
+       const auto key = generateAes(128);
+       const auto data = createRandom(128);
+       const auto iv = createRandom(Params::DEFAULT_AES_IV_LEN);
+       CryptoAlgorithm ca;
+       RawBuffer encrypted, decrypted;
+       auto shortIv = iv;
+       shortIv.resize(Params::DEFAULT_AES_GCM_IV_LEN);
+
+       ca.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GCM);
+       ca.setParam(ParamName::ED_IV, shortIv);
+       ca.setParam(ParamName::ED_TAG_LEN, 128);
+
+       // encrypt with 12B IV
+       BOOST_REQUIRE_NO_THROW(encrypted = key->encrypt(ca, data));
+
+       // decrypt with 12B IV
+       BOOST_REQUIRE_NO_THROW(decrypted = key->decrypt(ca, encrypted));
+       BOOST_REQUIRE(decrypted == data);
+
+       // decrypt with 16B IV should also succeed (workaround)
+       ca.setParam(ParamName::ED_IV, iv);
+       BOOST_REQUIRE_NO_THROW(decrypted = key->decrypt(ca, encrypted));
+       BOOST_REQUIRE(decrypted == data);
+
+       // encrypt with 16B IV
+       BOOST_REQUIRE_NO_THROW(encrypted = key->encrypt(ca, data));
+
+       // decrypt with 16B IV
+       BOOST_REQUIRE_NO_THROW(decrypted = key->decrypt(ca, encrypted));
+       BOOST_REQUIRE(decrypted == data);
+
+       // decrypt with 12B IV should fail
+       ca.setParam(ParamName::ED_IV, shortIv);
+       BOOST_REQUIRE_THROW(key->decrypt(ca, encrypted), Exc::Crypto::InputParam);
+}
+
 NEGATIVE_TEST_CASE(symmetricEncryptDecryptCtr)
 {
        const auto key = generateAes(128);