[Libreoffice-commits] core.git: config_host.mk.in configure.ac sal/CppunitTest_sal_rtl.mk sal/Library_sal.mk sal/qa sal/rtl

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Thu Aug 23 18:40:07 UTC 2018


 config_host.mk.in                |    1 
 configure.ac                     |   23 +++++
 sal/CppunitTest_sal_rtl.mk       |    4 
 sal/Library_sal.mk               |    8 +
 sal/qa/rtl/cipher/rtl_cipher.cxx |   61 ++++++++++++++
 sal/rtl/cipher.cxx               |  164 ++++++++++++++++++++++++++++++++++++++-
 6 files changed, 258 insertions(+), 3 deletions(-)

New commits:
commit 4bc16aeb73c1201f187742e0fefe35521fae77ac
Author:     Stephan Bergmann <sbergman at redhat.com>
AuthorDate: Wed Aug 22 09:49:25 2018 +0200
Commit:     Stephan Bergmann <sbergman at redhat.com>
CommitDate: Thu Aug 23 20:39:39 2018 +0200

    rhbz#1618703: Allow to use OpenSSL as backend for rtl/cipher.h
    
    ...with new configuration option --enable-cipher-openssl-backend
    
    rtl/cipher.h (which is part of the stable URE interface) offers functionality to
    en-/decrypt data with Blowfish in ECB, CBC, and streaming CFB mode, and with RC4
    (aka ARCFOUR; which is a stream cipher).  LO itself only uses Blowfish CFB and
    RC4, so only those are wired to OpenSSL for now, for simplicity.  Using Blowfish
    ECB and CBC, or Blowfish CFB in DirectionBoth mode would cause failures for now
    (cf. sal/qa/rtl/cipher/rtl_cipher.cxx); the assumption is that no external code
    actually makes use of this functionality.
    
    Using NSS instead of OpenSSL could be an alternative, but there appears to be no
    support in NSS for Blowfish in streaming CFB mode, only CKM_BLOWFISH_CBC for
    CBC mode.
    
    Change-Id: I0bc042961539ed46844c96cb1c808209578528a0
    Reviewed-on: https://gerrit.libreoffice.org/59428
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <sbergman at redhat.com>

diff --git a/config_host.mk.in b/config_host.mk.in
index b5eef155d524..38872e2330ae 100644
--- a/config_host.mk.in
+++ b/config_host.mk.in
@@ -115,6 +115,7 @@ export ENABLE_AVAHI=@ENABLE_AVAHI@
 export ENABLE_BREAKPAD=@ENABLE_BREAKPAD@
 export ENABLE_CAIRO_CANVAS=@ENABLE_CAIRO_CANVAS@
 export ENABLE_CHART_TESTS=@ENABLE_CHART_TESTS@
+export ENABLE_CIPHER_OPENSSL_BACKEND=@ENABLE_CIPHER_OPENSSL_BACKEND@
 export ENABLE_LIBCMIS=@ENABLE_LIBCMIS@
 export ENABLE_COINMP=@ENABLE_COINMP@
 export SYSTEM_COINMP=@SYSTEM_COINMP@
diff --git a/configure.ac b/configure.ac
index fbb74238379b..88dd8bfb3e1a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1480,6 +1480,11 @@ AC_ARG_ENABLE(openssl,
          use only if you are hacking on it.]),
 ,enable_openssl=yes)
 
+libo_FUZZ_ARG_ENABLE(cipher-openssl-backend,
+    AS_HELP_STRING([--enable-cipher-openssl-backend],
+        [Enable using OpenSSL as the actual implementation of the rtl/cipher.h functionality.
+         Requires --enable-openssl.]))
+
 AC_ARG_ENABLE(library-bin-tar,
     AS_HELP_STRING([--enable-library-bin-tar],
         [Enable the building and reused of tarball of binary build for some 'external' libraries.
@@ -9471,6 +9476,24 @@ fi
 
 AC_SUBST([DISABLE_OPENSSL])
 
+if test "$enable_cipher_openssl_backend" = yes && test "$DISABLE_OPENSSL" = TRUE; then
+    if test "$libo_fuzzed_enable_cipher_openssl_backend" = yes; then
+        AC_MSG_NOTICE([Resetting --enable-cipher-openssl-backend=no])
+        enable_cipher_openssl_backend=no
+    else
+        AC_MSG_ERROR([--enable-cipher-openssl-backend needs OpenSSL, but --disable-openssl was given.])
+    fi
+fi
+AC_MSG_CHECKING([whether to enable the OpenSSL backend for rtl/cipher.h])
+ENABLE_CIPHER_OPENSSL_BACKEND=
+if test "$enable_cipher_openssl_backend" = yes; then
+    AC_MSG_RESULT([yes])
+    ENABLE_CIPHER_OPENSSL_BACKEND=TRUE
+else
+    AC_MSG_RESULT([no])
+fi
+AC_SUBST([ENABLE_CIPHER_OPENSSL_BACKEND])
+
 dnl ===================================================================
 dnl Check for building gnutls
 dnl ===================================================================
diff --git a/sal/CppunitTest_sal_rtl.mk b/sal/CppunitTest_sal_rtl.mk
index 43533fc5ab1c..c2eaa72daa26 100644
--- a/sal/CppunitTest_sal_rtl.mk
+++ b/sal/CppunitTest_sal_rtl.mk
@@ -63,4 +63,8 @@ $(call gb_CppunitTest_get_target,sal_rtl) : \
 
 $(eval $(call gb_CppunitTest_use_external,sal_rtl,boost_headers))
 
+ifeq ($(ENABLE_CIPHER_OPENSSL_BACKEND),TRUE)
+$(eval $(call gb_CppunitTest_add_defs,sal_rtl,-DLIBO_CIPHER_OPENSSL_BACKEND))
+endif
+
 # vim: set noet sw=4 ts=4:
diff --git a/sal/Library_sal.mk b/sal/Library_sal.mk
index 9d5822e36256..868d9f8dd23d 100644
--- a/sal/Library_sal.mk
+++ b/sal/Library_sal.mk
@@ -254,4 +254,12 @@ $(eval $(call gb_Library_add_exception_objects,sal,\
 
 endif # ifneq ($(OS),WNT)
 
+ifeq ($(ENABLE_CIPHER_OPENSSL_BACKEND),TRUE)
+$(eval $(call gb_Library_add_defs,sal,-DLIBO_CIPHER_OPENSSL_BACKEND))
+$(eval $(call gb_Library_use_externals,sal, \
+    openssl \
+    openssl_headers \
+))
+endif
+
 # vim: set noet sw=4 ts=4:
diff --git a/sal/qa/rtl/cipher/rtl_cipher.cxx b/sal/qa/rtl/cipher/rtl_cipher.cxx
index e8877a92c5d5..57c22eb573ac 100644
--- a/sal/qa/rtl/cipher/rtl_cipher.cxx
+++ b/sal/qa/rtl/cipher/rtl_cipher.cxx
@@ -37,8 +37,12 @@ public:
     void create_001()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void create_002()
         {
@@ -48,8 +52,12 @@ public:
     void create_003()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeCBC);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void create_004()
         {
@@ -101,14 +109,22 @@ public:
     void createBF_001()
         {
             rtlCipher aCipher = rtl_cipher_createBF(rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void createBF_002()
         {
             rtlCipher aCipher = rtl_cipher_createBF(rtl_Cipher_ModeCBC);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void createBF_003()
         {
@@ -141,6 +157,12 @@ public:
     void test_encode(sal_uInt8 _nKeyValue, sal_uInt8 _nArgValue, rtl::OString const& _sPlainTextStr)
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+            (void) _nKeyValue;
+            (void) _nArgValue;
+            (void) _sPlainTextStr;
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -184,11 +206,18 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
 
     void test_encode_and_decode(sal_uInt8 _nKeyValue, sal_uInt8 _nArgValue, rtl::OString const& _sPlainTextStr)
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+            (void) _nKeyValue;
+            (void) _nArgValue;
+            (void) _sPlainTextStr;
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -236,6 +265,7 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
 
     void decode_001()
@@ -286,8 +316,12 @@ public:
     void destroy_001()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeCBC);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroy(aCipher);
+#endif
         }
     // Change the following lines only, if you add, remove or rename
     // member functions of the current class,
@@ -305,10 +339,14 @@ public:
     void destroyBF_001()
         {
             rtlCipher aCipher = rtl_cipher_createBF(rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
             rtl_cipher_destroyBF(aCipher);
             // more proforma
             // should not GPF
+#endif
         }
     // Change the following lines only, if you add, remove or rename
     // member functions of the current class,
@@ -326,6 +364,12 @@ public:
     void test_encode(sal_uInt8 _nKeyValue, sal_uInt8 _nArgValue, sal_uInt8 _nDataValue)
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+            (void) _nKeyValue;
+            (void) _nArgValue;
+            (void) _nDataValue;
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -360,6 +404,7 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
 
     void encode_001()
@@ -407,6 +452,9 @@ public:
     void init_001()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -424,11 +472,15 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
 
     void init_002()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -447,10 +499,14 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void init_003()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -469,10 +525,14 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
     void init_004()
         {
             rtlCipher aCipher = rtl_cipher_create(rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeECB);
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            CPPUNIT_ASSERT_EQUAL(rtlCipher(nullptr), aCipher);
+#else
             CPPUNIT_ASSERT_MESSAGE("create failed.", aCipher != nullptr);
 
             sal_uInt32     nKeyLen = 16;
@@ -492,6 +552,7 @@ public:
             delete [] pKeyBuffer;
 
             rtl_cipher_destroy(aCipher);
+#endif
         }
     // Change the following lines only, if you add, remove or rename
     // member functions of the current class,
diff --git a/sal/rtl/cipher.cxx b/sal/rtl/cipher.cxx
index bd913c38faf1..96cb896623e1 100644
--- a/sal/rtl/cipher.cxx
+++ b/sal/rtl/cipher.cxx
@@ -23,7 +23,15 @@
 #include <rtl/alloc.h>
 #include <rtl/cipher.h>
 #include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <limits>
 
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+#include <openssl/evp.h>
+#endif
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 #define RTL_CIPHER_NTOHL(c, l) \
     ((l)  = (static_cast<sal_uInt32>(*((c)++))) << 24, \
      (l) |= (static_cast<sal_uInt32>(*((c)++))) << 16, \
@@ -82,6 +90,7 @@
         case 1: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 24) & 0xff); \
     } \
 }
+#endif
 
 typedef rtlCipherError(cipher_init_t) (
     rtlCipher          Cipher,
@@ -183,6 +192,7 @@ void SAL_CALL rtl_cipher_destroy(rtlCipher Cipher) SAL_THROW_EXTERN_C()
         pImpl->m_delete(Cipher);
 }
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 #define CIPHER_ROUNDS_BF 16
 
 struct CipherKeyBF
@@ -190,9 +200,13 @@ struct CipherKeyBF
     sal_uInt32 m_S[4][256];
     sal_uInt32 m_P[CIPHER_ROUNDS_BF + 2];
 };
+#endif
 
 struct CipherContextBF
 {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    EVP_CIPHER_CTX * m_context;
+#else
     CipherKeyBF    m_key;
     union
     {
@@ -200,6 +214,7 @@ struct CipherContextBF
         sal_uInt8  m_byte[8];
     } m_iv;
     sal_uInt32     m_offset;
+#endif
 };
 
 struct CipherBF_Impl
@@ -208,11 +223,13 @@ struct CipherBF_Impl
     CipherContextBF m_context;
 };
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 static rtlCipherError BF_init(
     CipherContextBF *ctx,
     rtlCipherMode    eMode,
     const sal_uInt8 *pKeyData, sal_Size nKeyLen,
     const sal_uInt8 *pArgData, sal_Size nArgLen);
+#endif
 
 static rtlCipherError BF_update(
     CipherContextBF    *ctx,
@@ -221,6 +238,7 @@ static rtlCipherError BF_update(
     const sal_uInt8    *pData,   sal_Size nDatLen,
     sal_uInt8          *pBuffer, sal_Size nBufLen);
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 static void BF_updateECB(
     CipherContextBF    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 +627,9 @@ static const CipherKeyBF BF_key =
         0x9216D5D9L, 0x8979FB1BL
     }
 };
+#endif
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 static rtlCipherError BF_init(
     CipherContextBF *ctx,
     rtlCipherMode    eMode,
@@ -676,6 +696,7 @@ static rtlCipherError BF_init(
 
     return rtl_Cipher_E_None;
 }
+#endif
 
 static rtlCipherError BF_update(
     CipherContextBF    *ctx,
@@ -692,6 +713,31 @@ static rtlCipherError BF_update(
         return rtl_Cipher_E_BufferSize;
 
     /* Update. */
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    assert(eMode == rtl_Cipher_ModeStream);
+    (void) eDirection;
+    while (nDatLen > std::numeric_limits<int>::max()) {
+        int outl;
+        if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max())
+            == 0)
+        {
+            return rtl_Cipher_E_Unknown;
+        }
+        assert(outl == std::numeric_limits<int>::max());
+        pData += std::numeric_limits<int>::max();
+        nDatLen -= std::numeric_limits<int>::max();
+        pBuffer += std::numeric_limits<int>::max();
+    }
+    int outl;
+    if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0)
+    {
+        return rtl_Cipher_E_Unknown;
+    }
+    assert(outl == static_cast<int>(nDatLen));
+    // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h
+    // interface, and is hopefully not needed, as each individual Blowfish CFB update step doesn't
+    // hold back any data that would need to be finally flushed.
+#else
     if (eMode == rtl_Cipher_ModeECB)
     {
         /* Block mode. */
@@ -727,9 +773,11 @@ static rtlCipherError BF_update(
             pBuffer += 1;
         }
     }
+#endif
     return rtl_Cipher_E_None;
 }
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 static void BF_updateECB(
     CipherContextBF    *ctx,
     rtlCipherDirection  direction,
@@ -932,6 +980,7 @@ static sal_uInt32 BF(CipherKeyBF *key, sal_uInt32 x)
 
     return y;
 }
+#endif
 
 /**
     rtl_cipherBF (Blowfish) implementation.
@@ -944,6 +993,12 @@ rtlCipher SAL_CALL rtl_cipher_createBF(rtlCipherMode Mode) SAL_THROW_EXTERN_C()
 
     if (Mode == rtl_Cipher_ModeInvalid)
         return nullptr;
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    if (Mode != rtl_Cipher_ModeStream) {
+        // Cannot easily support ModeECB and ModeCBC, and they aren't used in the LO code at least:
+        return nullptr;
+    }
+#endif
 
     pImpl = static_cast<CipherBF_Impl*>(rtl_allocateZeroMemory(sizeof (CipherBF_Impl)));
     if (pImpl)
@@ -979,9 +1034,45 @@ rtlCipherError SAL_CALL rtl_cipher_initBF(
     else
         return rtl_Cipher_E_Direction;
 
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionBoth) {
+        // Cannot easily support DirectionBoth, and it isn't used in the LO code at least:
+        return rtl_Cipher_E_Direction;
+    }
+    if (nKeyLen > std::numeric_limits<int>::max()) {
+        return rtl_Cipher_E_BufferSize;
+    }
+    if (pImpl->m_context.m_context != nullptr) {
+        EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+    }
+    pImpl->m_context.m_context = EVP_CIPHER_CTX_new();
+    if (pImpl->m_context.m_context == nullptr) {
+        return rtl_Cipher_E_Memory;
+    }
+    unsigned char iv[8];
+    auto const n = std::min(nArgLen, sal_Size(8));
+    std::memcpy(iv, pArgData, n);
+    std::memset(iv + n, 0, 8 - n);
+    if (EVP_CipherInit_ex(
+            pImpl->m_context.m_context, EVP_bf_cfb(), nullptr, nullptr, iv,
+            pImpl->m_cipher.m_direction == rtl_Cipher_DirectionDecode ? 0 : 1)
+        == 0)
+    {
+        return rtl_Cipher_E_Unknown;
+    }
+    if (EVP_CIPHER_CTX_set_key_length(pImpl->m_context.m_context, static_cast<int>(nKeyLen)) == 0) {
+        return rtl_Cipher_E_Unknown;
+    }
+    if (EVP_CipherInit_ex(pImpl->m_context.m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0)
+    {
+        return rtl_Cipher_E_Unknown;
+    }
+    return rtl_Cipher_E_None;
+#else
     return BF_init(
         &(pImpl->m_context), pImpl->m_cipher.m_mode,
         pKeyData, nKeyLen, pArgData, nArgLen);
+#endif
 }
 
 rtlCipherError SAL_CALL rtl_cipher_encodeBF(
@@ -1038,18 +1129,31 @@ void SAL_CALL rtl_cipher_destroyBF(rtlCipher Cipher) SAL_THROW_EXTERN_C()
     if (pImpl)
     {
         if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmBF)
+        {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            if (pImpl->m_context.m_context != nullptr) {
+                EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+            }
+#endif
             rtl_freeZeroMemory(pImpl, sizeof(CipherBF_Impl));
+        }
         else
             rtl_freeMemory(pImpl);
     }
 }
 
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
 #define CIPHER_CBLOCK_ARCFOUR 256
+#endif
 
 struct ContextARCFOUR_Impl
 {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    EVP_CIPHER_CTX * m_context;
+#else
     unsigned int m_S[CIPHER_CBLOCK_ARCFOUR];
     unsigned int m_X, m_Y;
+#endif
 };
 
 struct CipherARCFOUR_Impl
@@ -1067,6 +1171,29 @@ static rtlCipherError rtl_cipherARCFOUR_init_Impl(
     ContextARCFOUR_Impl *ctx,
     const sal_uInt8     *pKeyData, sal_Size nKeyLen)
 {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    if (nKeyLen > std::numeric_limits<int>::max()) {
+        return rtl_Cipher_E_BufferSize;
+    }
+    if (ctx->m_context != nullptr) {
+        EVP_CIPHER_CTX_free(ctx->m_context);
+    }
+    ctx->m_context = EVP_CIPHER_CTX_new();
+    if (ctx->m_context == nullptr) {
+        return rtl_Cipher_E_Memory;
+    }
+    if (EVP_CipherInit_ex(ctx->m_context, EVP_rc4(), nullptr, nullptr, nullptr, 0) == 0) {
+            // RC4 en- and decryption is identical, so we can use 0=decrypt regardless of direction,
+            // and thus also support rtl_Cipher_DirectionBoth
+        return rtl_Cipher_E_Unknown;
+    }
+    if (EVP_CIPHER_CTX_set_key_length(ctx->m_context, static_cast<int>(nKeyLen)) == 0) {
+        return rtl_Cipher_E_Unknown;
+    }
+    if (EVP_CipherInit_ex(ctx->m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0) {
+        return rtl_Cipher_E_Unknown;
+    }
+#else
     unsigned int  K[CIPHER_CBLOCK_ARCFOUR];
     unsigned int *L, *S;
     unsigned int  x, y;
@@ -1107,6 +1234,7 @@ static rtlCipherError rtl_cipherARCFOUR_init_Impl(
     /* Initialize counters X and Y. */
     ctx->m_X = 0;
     ctx->m_Y = 0;
+#endif
 
     return rtl_Cipher_E_None;
 }
@@ -1116,9 +1244,6 @@ static rtlCipherError rtl_cipherARCFOUR_update_Impl(
     const sal_uInt8     *pData,   sal_Size nDatLen,
     sal_uInt8           *pBuffer, sal_Size nBufLen)
 {
-    unsigned int *S;
-    sal_Size k;
-
     /* Check arguments. */
     if (!pData || !pBuffer)
         return rtl_Cipher_E_Argument;
@@ -1126,6 +1251,31 @@ static rtlCipherError rtl_cipherARCFOUR_update_Impl(
     if (!((0 < nDatLen) && (nDatLen <= nBufLen)))
         return rtl_Cipher_E_BufferSize;
 
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+    while (nDatLen > std::numeric_limits<int>::max()) {
+        int outl;
+        if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max())
+            == 0)
+        {
+            return rtl_Cipher_E_Unknown;
+        }
+        assert(outl == std::numeric_limits<int>::max());
+        pData += std::numeric_limits<int>::max();
+        nDatLen -= std::numeric_limits<int>::max();
+        pBuffer += std::numeric_limits<int>::max();
+    }
+    int outl;
+    if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0) {
+        return rtl_Cipher_E_Unknown;
+    }
+    assert(outl == static_cast<int>(nDatLen));
+    // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h
+    // interface, and is hopefully not needed, as each individual RC4 update step doesn't hold back
+    // any data that would need to be finally flushed.
+#else
+    unsigned int *S;
+    sal_Size k;
+
     /* Update. */
     S = &(ctx->m_S[0]);
     for (k = 0; k < nDatLen; k++)
@@ -1147,6 +1297,7 @@ static rtlCipherError rtl_cipherARCFOUR_update_Impl(
         t = (S[x] + S[y]) % CIPHER_CBLOCK_ARCFOUR;
         pBuffer[k] = pData[k] ^ static_cast<sal_uInt8>(S[t] & 0xff);
     }
+#endif
 
     return rtl_Cipher_E_None;
 }
@@ -1249,7 +1400,14 @@ void SAL_CALL rtl_cipher_destroyARCFOUR(rtlCipher Cipher) SAL_THROW_EXTERN_C()
     if (pImpl)
     {
         if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmARCFOUR)
+        {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+            if (pImpl->m_context.m_context != nullptr) {
+                EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+            }
+#endif
             rtl_freeZeroMemory(pImpl, sizeof(CipherARCFOUR_Impl));
+        }
         else
             rtl_freeMemory(pImpl);
     }


More information about the Libreoffice-commits mailing list