Forráskód Böngészése

HPCC-20383 Add RSA and AES crypto helper functions and unit tests

Signed-off-by: Jake Smith <jake.smith@lexisnexisrisk.com>
Jake Smith 6 éve
szülő
commit
c57442e203

+ 2 - 2
dali/base/CMakeLists.txt

@@ -64,7 +64,7 @@ include_directories (
          ./../../system/jlib 
          ./../../rtl/include 
          ./../../system/security/shared
-         ./../../system/security/digisign
+         ./../../system/security/cryptohelper
     )
 
 ADD_DEFINITIONS( -DLOGMSGCOMPONENT=3 -D_USRDLL -DDALI_EXPORTS )
@@ -79,7 +79,7 @@ if(NOT PLUGIN)
     target_link_libraries(
         dalibase 
         jlib
-        digisign
+        cryptohelper
         mp 
         hrpc 
         remote)

+ 2 - 0
dali/base/dasess.cpp

@@ -35,6 +35,8 @@
 #include "dasess.hpp"
 #include "digisign.hpp"
 
+using namespace cryptohelper;
+
 #ifdef _MSC_VER
 #pragma warning (disable : 4355)
 #endif

+ 2 - 2
dali/server/CMakeLists.txt

@@ -38,7 +38,7 @@ include_directories (
          ./../../system/include 
          ./../../system/security/shared 
          ./../../system/security/LdapSecurity 
-         ./../../system/security/digisign
+         ./../../system/security/cryptohelper
          ./../../system/jlib 
          ${CMAKE_BINARY_DIR}
          ${CMAKE_BINARY_DIR}/oss
@@ -61,7 +61,7 @@ target_link_libraries ( daserver
          hrpc 
          remote 
          dalibase 
-         digisign
+         cryptohelper
     )
 IF (USE_OPENLDAP)
 target_link_libraries ( daserver LdapSecurity )

+ 2 - 0
dali/server/daldap.cpp

@@ -27,6 +27,8 @@
 #include "dautils.hpp"
 #include "digisign.hpp"
 
+using namespace cryptohelper;
+
 #ifndef _NO_LDAP
 #include "seclib.hpp"
 #include "ldapsecurity.hpp"

+ 36 - 0
system/jlib/jutil.cpp

@@ -901,6 +901,42 @@ IRandomNumberGenerator *createRandomNumberGenerator()
     return new CRandom();
 }
 
+void fillRandomData(size32_t writeSz, void *_writePtr)
+{
+#ifdef __APPLE__
+//Apple does not currently support thread_local (very strange), so need to use __thread.
+static __thread Owned<IRandomNumberGenerator> generator = createRandomNumberGenerator();
+#else
+static thread_local Owned<IRandomNumberGenerator> generator = createRandomNumberGenerator();
+#endif
+    unsigned *writePtr = (unsigned *)_writePtr;
+    unsigned *bufEnd = (unsigned *)(((byte *)writePtr)+writeSz);
+    while (true)
+    {
+        size32_t diff = (const byte *)bufEnd - (const byte *)writePtr;
+        unsigned r = generator->next();
+        if (diff<sizeof(unsigned))
+        {
+            // last few bytes
+            byte *p = (byte *)writePtr;
+            while (diff--)
+            {
+                *p++ = r & 0xff;
+                r >>= 8;
+            }
+            break;
+        }
+        *writePtr++ = r;
+    }
+}
+
+void fillRandomData(size32_t writeSz, MemoryBuffer &mb)
+{
+    void *writePtr = mb.reserveTruncate(writeSz);
+    fillRandomData(writeSz, writePtr);
+}
+
+
 #ifdef WIN32
 // This function has the same prototype for rand_r, but seed is ignored.
 

+ 5 - 0
system/jlib/jutil.hpp

@@ -128,6 +128,11 @@ interface IRandomNumberGenerator: public IInterface
 
 extern jlib_decl IRandomNumberGenerator *createRandomNumberGenerator();
 
+// functions to populate buffers with randomly generated data
+extern jlib_decl void fillRandomData(size32_t writeSz, void *writePtr);
+extern jlib_decl void fillRandomData(size32_t writeSz, MemoryBuffer &mb);
+
+
 #ifdef WIN32
 
 // Reentrant version of the rand() function for use with multithreaded applications.

+ 1 - 1
system/security/CMakeLists.txt

@@ -24,7 +24,7 @@ if (USE_ZLIB)
   HPCC_ADD_SUBDIRECTORY (zcrypt)
 endif()
 
-HPCC_ADD_SUBDIRECTORY (digisign)
+HPCC_ADD_SUBDIRECTORY (cryptohelper)
 
 IF (USE_APR)
   HPCC_ADD_SUBDIRECTORY (plugins/htpasswdSecurity)

+ 2 - 2
system/security/LdapSecurity/CMakeLists.txt

@@ -41,7 +41,7 @@ set (    SRCS
 include_directories ( 
          ./../../include 
          ./../shared 
-         ./../digisign
+         ./../cryptohelper
          ./../../jlib 
          ./../../../esp/platform 
 	     ./../../../dali/base
@@ -55,7 +55,7 @@ HPCC_ADD_LIBRARY( LdapSecurity SHARED ${SRCS} )
 install ( TARGETS LdapSecurity RUNTIME DESTINATION ${EXEC_DIR} LIBRARY DESTINATION ${LIB_DIR} )
 target_link_libraries ( LdapSecurity
          jlib
-         digisign
+         cryptohelper
          dalibase
          ${OPENLDAP_LIBRARIES}
     )

+ 3 - 0
system/security/LdapSecurity/ldapsecurity.cpp

@@ -22,6 +22,9 @@
 #include "authmap.ipp"
 #include "digisign.hpp"
 
+using namespace cryptohelper;
+
+
 /**********************************************************
  *     CLdapSecUser                                       *
  **********************************************************/

+ 11 - 8
system/security/digisign/CMakeLists.txt

@@ -14,20 +14,23 @@
 #    limitations under the License.
 ################################################################################
 
-# Component: digisign
+# Component: cryptohelper
 
 #####################################################
 # Description:
 # ------------
-#    Cmake Input File for digisign
+#    Cmake Input File for cryptohelper
 #####################################################
 
 
-project( digisign )
+project( cryptohelper )
 
 set (
         SRCS
+        cryptocommon.cpp
         digisign.cpp
+        pke.cpp
+        ske.cpp
     )
 
 include_directories (
@@ -37,24 +40,24 @@ include_directories (
         ${OPENSSL_INCLUDE_DIR}
     )
 
-ADD_DEFINITIONS ( -DDIGISIGN_EXPORTS -D_USRDLL )
+ADD_DEFINITIONS ( -DCRYPTOHELPER_EXPORTS -D_USRDLL )
 
-HPCC_ADD_LIBRARY( digisign SHARED ${SRCS} )
+HPCC_ADD_LIBRARY( cryptohelper SHARED ${SRCS} )
 
 install (
-        TARGETS digisign
+        TARGETS cryptohelper
         RUNTIME DESTINATION ${EXEC_DIR}
         LIBRARY DESTINATION ${LIB_DIR}
     )
 
 target_link_libraries (
-        digisign
+        cryptohelper
         jlib
     )
 
 if (USE_OPENSSL)
       target_link_libraries (
-          digisign
+          cryptohelper
           ${OPENSSL_LIBRARIES}
     )
 endif()

+ 92 - 0
system/security/cryptohelper/cryptocommon.cpp

@@ -0,0 +1,92 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#include "jliball.hpp"
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include "cryptocommon.hpp"
+
+
+static void addAlgorithms()
+{
+    OpenSSL_add_all_algorithms();
+}
+
+MODULE_INIT(INIT_PRIORITY_STANDARD)
+{
+    addAlgorithms();
+    return true;
+}
+MODULE_EXIT()
+{
+}
+
+
+namespace cryptohelper
+{
+
+static void populateErrorFormat(StringBuffer &formatMessage, const char *format)
+{
+    // openssl doesn't define max error string size, but 1K will do
+    char *evpErr = formatMessage.reserve(1024);
+    ERR_error_string_n(ERR_get_error(), evpErr, 1024);
+    formatMessage.setLength(strlen(evpErr));
+    formatMessage.append(" - ").append(format);
+}
+IException *makeEVPExceptionVA(int code, const char *format, va_list args)
+{
+    StringBuffer formatMessage;
+    populateErrorFormat(formatMessage, format);
+    return makeStringExceptionVA(code, formatMessage, args);
+}
+
+IException *makeEVPException(int code, const char *msg)
+{
+    StringBuffer formatMessage;
+    populateErrorFormat(formatMessage, msg);
+    return makeStringException(code, formatMessage);
+}
+
+IException *makeEVPExceptionV(int code, const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    IException *e = makeEVPExceptionVA(code, format, args);
+    va_end(args);
+    return e;
+}
+
+void throwEVPException(int code, const char *format)
+{
+    throw makeEVPException(code, format);
+}
+
+void throwEVPExceptionV(int code, const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    IException *e = makeEVPExceptionVA(code, format, args);
+    va_end(args);
+    throw e;
+}
+
+} // end of namespace cryptohelper
+
+#endif // #if defined(_USE_OPENSSL) && !defined(_WIN32)

+ 60 - 0
system/security/cryptohelper/cryptocommon.hpp

@@ -0,0 +1,60 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#ifndef CRYPTOCOMMON_HPP
+#define CRYPTOCOMMON_HPP
+
+#ifndef CRYPTOHELPER_API
+
+#ifndef CRYPTOHELPER_EXPORTS
+    #define CRYPTOHELPER_API DECL_IMPORT
+#else
+    #define CRYPTOHELPER_API DECL_EXPORT
+#endif //CRYPTOHELPER_EXPORTS
+
+#endif
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+namespace cryptohelper
+{
+
+CRYPTOHELPER_API IException *makeEVPException(int code, const char *msg);
+CRYPTOHELPER_API IException *makeEVPExceptionV(int code, const char *format, ...) __attribute__((format(printf, 2, 3)));
+CRYPTOHELPER_API void throwEVPException(int code, const char *format);
+CRYPTOHELPER_API void throwEVPExceptionV(int code, const char *format, ...) __attribute__((format(printf, 2, 3)));
+
+inline void voidBIOfree(BIO *bio) { BIO_free(bio); }
+inline void voidOpenSSLFree(void *m) { OPENSSL_free(m); }
+
+typedef OwnedPtrCustomFree<BIO, voidBIOfree> OwnedEVPBio;
+typedef OwnedPtrCustomFree<EVP_PKEY, EVP_PKEY_free> OwnedEVPPkey;
+typedef OwnedPtrCustomFree<EVP_PKEY_CTX, EVP_PKEY_CTX_free> OwnedEVPPkeyCtx;
+typedef OwnedPtrCustomFree<void, voidOpenSSLFree> OwnedEVPMemory;
+typedef OwnedPtrCustomFree<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> OwnedEVPCipherCtx;
+typedef OwnedPtrCustomFree<RSA, RSA_free> OwnedEVPRSA;
+
+} // end of namespace cryptohelper
+
+#endif // end of #if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#endif
+

+ 20 - 23
system/security/digisign/digisign.cpp

@@ -21,21 +21,19 @@
 #include <openssl/evp.h>
 #endif
 #include "jencrypt.hpp"
+#include "cryptocommon.hpp"
 #include "digisign.hpp"
 #include <mutex>
 
 
+namespace cryptohelper
+{
+
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
 
 #define EVP_CLEANUP(key,ctx) EVP_PKEY_free(key);       \
                              EVP_MD_CTX_destroy(ctx);
 
-#define EVP_THROW(str) {                                            \
-                         char buff[120];                            \
-                         ERR_error_string(ERR_get_error(), buff);   \
-                         throw MakeStringException(-1, str, buff);  \
-                       }
-
 class CDigitalSignatureManager : implements IDigitalSignatureManager, public CInterface
 {
 private:
@@ -54,9 +52,7 @@ private:
         //create an RSA object from public key
         BIO * keybio = BIO_new_mem_buf((void*) keyBuff, -1);
         if (nullptr == keybio)
-        {
-            EVP_THROW("digiSign:BIO_new_mem_buf: %s");
-        }
+            throwEVPException(-1, "digiSign:BIO_new_mem_buf");
 
         RSA * rsa;
         if (isSigning)
@@ -72,16 +68,16 @@ private:
         if (nullptr == rsa)
         {
             if (isSigning)
-                EVP_THROW("digiSign:PEM_read_bio_RSAPrivateKey: %s")
+                throwEVPException(-1, "digiSign:PEM_read_bio_RSAPrivateKey");
             else
-                EVP_THROW("digiSign:PEM_read_bio_RSA_PUBKEY: %s")
+                throwEVPException(-1, "digiSign:PEM_read_bio_RSA_PUBKEY");
         }
 
         EVP_PKEY* pKey = EVP_PKEY_new();
         if (nullptr == pKey)
         {
             RSA_free(rsa);
-            EVP_THROW("digiSign:EVP_PKEY_new: %s");
+            throwEVPException(-1, "digiSign:EVP_PKEY_new");
         }
         EVP_PKEY_assign_RSA(pKey, rsa);//take ownership of the rsa. pKey will free rsa
 
@@ -89,7 +85,7 @@ private:
         if (nullptr == RSACtx)
         {
             EVP_PKEY_free(pKey);
-            EVP_THROW("digiSign:EVP_MD_CTX_create: %s");
+            throwEVPException(-1, "digiSign:EVP_MD_CTX_create");
         }
 
         //initialize context for SHA-256 hashing function
@@ -102,9 +98,9 @@ private:
         {
             EVP_CLEANUP(pKey, RSACtx);//cleans allocated key and digest context
             if (isSigning)
-                EVP_THROW("digiSign:EVP_DigestSignInit: %s")
+                throwEVPException(-1, "digiSign:EVP_DigestSignInit");
             else
-                EVP_THROW("digiSign:EVP_DigestVerifyInit: %s")
+                throwEVPException(-1, "digiSign:EVP_DigestVerifyInit");
         }
         *ctx = RSACtx;
         *PKey = pKey;
@@ -148,7 +144,7 @@ public:
         if (EVP_DigestSignUpdate(signingCtx, (size_t*)text, strlen(text)) <= 0)
         {
             EVP_CLEANUP(signingKey, signingCtx);
-            EVP_THROW("digiSign:EVP_DigestSignUpdate: %s");
+            throwEVPException(-1, "digiSign:EVP_DigestSignUpdate");
         }
 
         //compute length of signature
@@ -156,13 +152,13 @@ public:
         if (EVP_DigestSignFinal(signingCtx, nullptr, &encMsgLen) <= 0)
         {
             EVP_CLEANUP(signingKey, signingCtx);
-            EVP_THROW("digiSign:EVP_DigestSignFinal1: %s");
+            throwEVPException(-1, "digiSign:EVP_DigestSignFinal1");
         }
 
         if (encMsgLen == 0)
         {
             EVP_CLEANUP(signingKey, signingCtx);
-            EVP_THROW("digiSign:EVP_DigestSignFinal length returned 0: %s");
+            throwEVPException(-1, "digiSign:EVP_DigestSignFinal length returned 0");
         }
 
         //compute signature (signed digest)
@@ -177,7 +173,7 @@ public:
         {
             free(encMsg);
             EVP_CLEANUP(signingKey, signingCtx);
-            EVP_THROW("digiSign:EVP_DigestSignFinal2: %s");
+            throwEVPException(-1, "digiSign:EVP_DigestSignFinal2");
         }
 
 
@@ -208,7 +204,7 @@ public:
         if (EVP_DigestVerifyUpdate(verifyingCtx, text, strlen(text)) <= 0)
         {
             EVP_CLEANUP(verifyingKey, verifyingCtx);
-            EVP_THROW("digiVerify:EVP_DigestVerifyUpdate: %s");
+            throwEVPException(-1, "digiVerify:EVP_DigestVerifyUpdate");
         }
 
         int match = EVP_DigestVerifyFinal(verifyingCtx, (unsigned char *)decodedSig.str(), decodedSig.length());
@@ -291,7 +287,7 @@ static void addAlgorithms()
 extern "C"
 {
     //Returns reference to singleton instance created from environment.conf key file settings
-    DIGISIGN_API IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv()
+    CRYPTOHELPER_API IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv()
     {
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
         std::call_once(dsmInitFlag, createDigitalSignatureManagerInstance, &dsm);
@@ -303,7 +299,7 @@ extern "C"
 
     //Create using given key filespecs
     //Caller must release when no longer needed
-    DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase)
+    CRYPTOHELPER_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase)
     {
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
         StringBuffer privateKeyBuff;
@@ -345,7 +341,7 @@ extern "C"
 
     //Create using given PEM formatted keys
     //Caller must release when no longer needed
-    DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase)
+    CRYPTOHELPER_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase)
     {
 #if defined(_USE_OPENSSL) && !defined(_WIN32)
         std::call_once(dsmAddAlgoFlag, addAlgorithms);
@@ -356,3 +352,4 @@ extern "C"
     }
 }
 
+} // namespace cryptohelper

+ 13 - 8
system/security/digisign/digisign.hpp

@@ -17,16 +17,19 @@
 #ifndef DIGISIGN_HPP
 #define DIGISIGN_HPP
 
-#ifndef DIGISIGN_API
+#ifndef CRYPTOHELPER_API
 
-#ifndef DIGISIGN_EXPORTS
-    #define DIGISIGN_API DECL_IMPORT
+#ifndef CRYPTOHELPER_EXPORTS
+    #define CRYPTOHELPER_API DECL_IMPORT
 #else
-    #define DIGISIGN_API DECL_EXPORT
-#endif //DIGISIGN_EXPORTS
+    #define CRYPTOHELPER_API DECL_EXPORT
+#endif //CRYPTOHELPER_EXPORTS
 
 #endif
 
+namespace cryptohelper
+{
+
 //General purpose digital signature manager
 //Useful to sign a text string, so the consumer can be assured it has not been altered
 interface IDigitalSignatureManager : extends IInterface //Public/Private key message signer/verifyer
@@ -41,14 +44,16 @@ public:
 extern "C"
 {
     //Uses the HPCCPublicKey/HPCCPrivateKey key files specified in environment.conf
-    DIGISIGN_API IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv();
+    CRYPTOHELPER_API IDigitalSignatureManager * queryDigitalSignatureManagerInstanceFromEnv();
 
     //Create using the given key files
-    DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase);
+    CRYPTOHELPER_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromFiles(const char * _pubKey, const char *_privKey, const char * _passPhrase);
 
     //Create using the given PEM formatted keys
-    DIGISIGN_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase);
+    CRYPTOHELPER_API IDigitalSignatureManager * createDigitalSignatureManagerInstanceFromKeys(StringBuffer & _pubKeyBuff, StringBuffer & _privKeyBuff, const char * _passPhrase);
 }
 
+} // namespace cryptohelper
+
 #endif
 

+ 225 - 0
system/security/cryptohelper/pke.cpp

@@ -0,0 +1,225 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+/*
+ * Module dealing with openssl asymmetric key encryption/decription.
+ * For now just RSA.
+ */
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#include "jliball.hpp"
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "cryptocommon.hpp"
+#include "pke.hpp"
+
+namespace cryptohelper
+{
+
+void CLoadedKey::loadKeyBio(const char *keyMem)
+{
+    keyBio.setown(BIO_new_mem_buf(keyMem, -1));
+    if (!keyBio)
+        throwEVPException(0, "loadKeyBio: Failed to create bio for key");
+}
+
+bool CLoadedKey::loadKeyFileToMem(MemoryBuffer &keyMb, const char *keyFile)
+{
+    OwnedIFile iFile = createIFile(keyFile);
+    OwnedIFileIO iFileIO = iFile->open(IFOread);
+    if (!iFileIO)
+        return false;
+    size32_t sz = iFile->size();
+    verifyex(read(iFileIO, 0, sz, keyMb) == sz);
+    return true;
+}
+
+void CLoadedKey::finalize(RSA *_rsa, const char *_keyName)
+{
+    rsa.setown(_rsa);
+    key.setown(EVP_PKEY_new());
+    EVP_PKEY_set1_RSA(key, _rsa);
+    keyName.set(_keyName);
+}
+
+class CLoadedPublicKeyFromFile : public CLoadedKey
+{
+public:
+    CLoadedPublicKeyFromFile(const char *keyFile, const char *passPhrase)
+    {
+        MemoryBuffer keyMb;
+        if (!loadKeyFileToMem(keyMb, keyFile))
+            throwEVPExceptionV(0, "loadKeyFileToMem: failed to open key: %s", keyFile);
+        loadKeyBio((const char *)keyMb.bytes());
+        RSA *rsaKey = PEM_read_bio_RSA_PUBKEY(keyBio, nullptr, nullptr, (void*)passPhrase);
+        if (!rsaKey)
+            throwEVPExceptionV(0, "Failed to public create key: %s", keyFile);
+        finalize(rsaKey, keyFile);
+    }
+};
+
+class CLoadedPublicKeyFromMemory : public CLoadedKey
+{
+public:
+    CLoadedPublicKeyFromMemory(const char *key, const char *passPhrase)
+    {
+        loadKeyBio(key);
+        RSA *rsaKey = PEM_read_bio_RSA_PUBKEY(keyBio, nullptr, nullptr, (void*)passPhrase);
+        if (!rsaKey)
+            throwEVPException(0, "Failed to create public key");
+        finalize(rsaKey, "<inline>");
+    }
+};
+
+class CLoadedPrivateKeyFromFile : public CLoadedKey
+{
+public:
+    CLoadedPrivateKeyFromFile(const char *keyFile, const char *passPhrase)
+    {
+        MemoryBuffer keyMb;
+        if (!loadKeyFileToMem(keyMb, keyFile))
+            throwEVPException(0, "loadKeyFileToMem: failed to open private key");
+        loadKeyBio((const char *)keyMb.bytes());
+        RSA *rsaKey = PEM_read_bio_RSAPrivateKey(keyBio, nullptr, nullptr, (void*)passPhrase);
+        if (!rsaKey)
+            throwEVPException(0, "Failed to create private key");
+        finalize(rsaKey, keyFile);
+    }
+};
+
+class CLoadedPrivateKeyFromMemory : public CLoadedKey
+{
+public:
+    CLoadedPrivateKeyFromMemory(const char *key, const char *passPhrase)
+    {
+        loadKeyBio(key);
+        RSA *rsaKey = PEM_read_bio_RSAPrivateKey(keyBio, nullptr, nullptr, (void*)passPhrase);
+        if (!rsaKey)
+            throwEVPException(0, "Failed to create private key");
+        finalize(rsaKey, "<inline>");
+    }
+};
+
+CLoadedKey *loadPublicKeyFromFile(const char *keyFile, const char *passPhrase)
+{
+    return new CLoadedPublicKeyFromFile(keyFile, passPhrase);
+}
+
+CLoadedKey *loadPublicKeyFromMemory(const char *key, const char *passPhrase)
+{
+    return new CLoadedPublicKeyFromMemory(key, passPhrase);
+}
+
+CLoadedKey *loadPrivateKeyFromFile(const char *keyFile, const char *passPhrase)
+{
+    return new CLoadedPrivateKeyFromFile(keyFile, passPhrase);
+}
+
+CLoadedKey *loadPrivateKeyFromMemory(const char *key, const char *passPhrase)
+{
+    return new CLoadedPrivateKeyFromMemory(key, passPhrase);
+}
+
+size32_t _publicKeyEncrypt(OwnedEVPMemory &dstMem, size32_t dstMaxSz, size32_t inSz, const void *inBytes, const CLoadedKey &publicKey)
+{
+    OwnedEVPPkeyCtx ctx(EVP_PKEY_CTX_new(publicKey, nullptr));
+    if (!ctx || (EVP_PKEY_encrypt_init(ctx) <= 0) || (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)) <= 0)
+        throwEVPExceptionV(0, "publicKeyEncrypt: failed to initialize key: %s", publicKey.queryKeyName());
+
+    /* Determine buffer length */
+    size_t outLen;
+    if (EVP_PKEY_encrypt(ctx, nullptr, &outLen, (unsigned char *)inBytes, inSz) <= 0)
+        throwEVPExceptionV(0, "publicKeyEncrypt: [EVP_PKEY_encrypt] failed to encrypt with key: %s", publicKey.queryKeyName());
+
+    if (dstMaxSz && outLen > dstMaxSz)
+        return 0;
+    void *dst = OPENSSL_malloc(outLen);
+    if (!dst)
+        throwEVPExceptionV(0, "publicKeyEncrypt: [OPENSSL_malloc] failed with key: %s", publicKey.queryKeyName());
+
+    if (EVP_PKEY_encrypt(ctx, (unsigned char *)dst, &outLen, (unsigned char *)inBytes, inSz) <= 0)
+        throwEVPExceptionV(0, "publicKeyEncrypt: [EVP_PKEY_encrypt] failed to encrypt with key: %s", publicKey.queryKeyName());
+
+    dstMem.setown(dst);
+    return (size32_t)outLen;
+}
+
+size32_t publicKeyEncrypt(void *dst, size32_t dstMaxSz, size32_t inSz, const void *inBytes, const CLoadedKey &publicKey)
+{
+    OwnedEVPMemory encrypted;
+    size32_t encryptedSz = _publicKeyEncrypt(encrypted, dstMaxSz, inSz, inBytes, publicKey);
+    if (encryptedSz)
+        memcpy(dst, encrypted.get(), encryptedSz);
+    return encryptedSz;
+}
+
+size32_t publicKeyEncrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &publicKey)
+{
+    OwnedEVPMemory encrypted;
+    size32_t encryptedSz = _publicKeyEncrypt(encrypted, 0, inSz, inBytes, publicKey);
+    out.append(encryptedSz, encrypted);
+    return encryptedSz;
+}
+
+size32_t _privateKeyDecrypt(OwnedEVPMemory &dstMem, size32_t dstMaxSz, size32_t inSz, const void *inBytes, const CLoadedKey &privateKey)
+{
+    OwnedEVPPkeyCtx ctx(EVP_PKEY_CTX_new(privateKey, nullptr));
+    if (!ctx || (EVP_PKEY_decrypt_init(ctx) <= 0) || (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)) <= 0)
+        throwEVPException(0, "privateKeyDecrypt: [EVP_PKEY_decrypt_init] failed");
+
+    /* Determine buffer length */
+    size_t outLen;
+    if (EVP_PKEY_decrypt(ctx, nullptr, &outLen, (const unsigned char *)inBytes, inSz) <= 0)
+        throwEVPException(0, "privateKeyDecrypt: [EVP_PKEY_decrypt] failed to decrypt");
+
+    if (dstMaxSz && outLen > dstMaxSz)
+        return 0;
+    void *dst = OPENSSL_malloc(outLen);
+    if (!dst)
+        throwEVPException(0, "privateKeyDecrypt: [OPENSSL_malloc] failed");
+
+    if (EVP_PKEY_decrypt(ctx, (unsigned char *)dst, &outLen, (const unsigned char *)inBytes, inSz) <= 0)
+        throwEVPException(0, "privateKeyDecrypt: [EVP_PKEY_decrypt] failed to decrypt");
+
+    dstMem.setown(dst);
+    return (size32_t)outLen;
+}
+
+size32_t privateKeyDecrypt(void *dst, size32_t dstMaxSz, size32_t inSz, const void *inBytes, const CLoadedKey &privateKey)
+{
+    OwnedEVPMemory decrypted;
+    size32_t decryptedSz = _privateKeyDecrypt(decrypted, dstMaxSz, inSz, inBytes, privateKey);
+    if (decryptedSz)
+        memcpy(dst, decrypted.get(), decryptedSz);
+    return decryptedSz;
+}
+
+size32_t privateKeyDecrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &privateKey)
+{
+    OwnedEVPMemory decrypted;
+    size32_t decryptedSz = _privateKeyDecrypt(decrypted, 0, inSz, inBytes, privateKey);
+    out.append(decryptedSz, decrypted);
+    return decryptedSz;
+}
+
+} // end of namespace cryptohelper
+
+#endif // end of #if defined(_USE_OPENSSL) && !defined(_WIN32)
+

+ 79 - 0
system/security/cryptohelper/pke.hpp

@@ -0,0 +1,79 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+// Public-key encryption
+
+#ifndef PKE_HPP
+#define PKE_HPP
+
+#ifndef CRYPTOHELPER_API
+
+#ifndef CRYPTOHELPER_EXPORTS
+    #define CRYPTOHELPER_API DECL_IMPORT
+#else
+    #define CRYPTOHELPER_API DECL_EXPORT
+#endif //CRYPTOHELPER_EXPORTS
+
+#endif
+
+#include "cryptocommon.hpp"
+
+namespace cryptohelper
+{
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+class CLoadedKey : public CSimpleInterfaceOf<IInterface>
+{
+protected:
+    OwnedEVPRSA rsa;
+    OwnedEVPBio keyBio;
+    OwnedEVPPkey key;
+    StringAttr keyName;
+
+    void loadKeyBio(const char *keyMem);
+    bool loadKeyFileToMem(MemoryBuffer &keyMb, const char *keyFile);
+    void finalize(RSA *rsaKey, const char *keyName);
+public:
+    CLoadedKey() { }
+    inline EVP_PKEY *get() const           { return key; }
+    inline EVP_PKEY * operator -> () const { return key; }
+    inline operator EVP_PKEY *() const     { return key; }
+    const char *queryKeyName() const  { return keyName; }
+};
+
+CRYPTOHELPER_API CLoadedKey *loadPublicKeyFromFile(const char *keyFile, const char *passPhrase);
+CRYPTOHELPER_API CLoadedKey *loadPublicKeyFromMemory(const char *key, const char *passPhrase);
+CRYPTOHELPER_API CLoadedKey *loadPrivateKeyFromFile(const char *keyFile, const char *passPhrase);
+CRYPTOHELPER_API CLoadedKey *loadPrivateKeyFromMemory(const char *key, const char *passPhrase);
+
+CRYPTOHELPER_API size32_t publicKeyEncrypt(MemoryBuffer &out, size32_t inLen, const void *inBytes, const CLoadedKey &key);
+CRYPTOHELPER_API size32_t privateKeyDecrypt(MemoryBuffer &out, size32_t inLen, const void *inBytes, const CLoadedKey &key);
+CRYPTOHELPER_API size32_t publicKeyEncrypt(void *dst, size32_t dstMaxSz, size32_t inLen, const void *inBytes, const CLoadedKey &key);
+CRYPTOHELPER_API size32_t privateKeyDecrypt(void *dst, size32_t dstMaxSz, size32_t inLen, const void *inBytes, const CLoadedKey &key);
+
+
+#endif // end of #if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+} // end of namespace cryptohelper
+
+
+#endif // PKE_HPP
+

+ 161 - 0
system/security/cryptohelper/ske.cpp

@@ -0,0 +1,161 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+/*
+ * Module dealing with openssl symmetric key encryption/decription.
+ * For now just AES.
+ */
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+#include "jliball.hpp"
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "cryptocommon.hpp"
+#include "pke.hpp"
+#include "ske.hpp"
+
+namespace cryptohelper
+{
+
+size32_t aesKeyEncrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const char key[aesKeySize], const char iv[aesBlockSize])
+{
+    OwnedEVPCipherCtx ctx(EVP_CIPHER_CTX_new());
+    if (!ctx)
+        throw makeEVPException(0, "Failed EVP_CIPHER_CTX_new");
+
+    /* Initialise the encryption operation. IMPORTANT - ensure you use a key
+     * and IV size appropriate for your cipher
+     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
+     * IV size for *most* modes is the same as the block size. For AES this
+     * is 128 bits
+     * */
+    if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, (const unsigned char *)key, (const unsigned char *)iv))
+        throw makeEVPException(0, "Failed EVP_EncryptInit_ex");
+
+    /* Provide the message to be encrypted, and obtain the encrypted output.
+     * EVP_EncryptUpdate can be called multiple times if necessary
+     */
+
+    const size32_t cipherBlockSz = 128;
+    size32_t outMaxSz = inSz + cipherBlockSz/8;
+    size32_t startSz = out.length();
+    byte *outPtr = (byte *)out.reserveTruncate(outMaxSz);
+    int outSz;
+    if (1 != EVP_EncryptUpdate(ctx, (unsigned char *)outPtr, &outSz, (unsigned char *)inBytes, inSz))
+        throw makeEVPException(0, "Failed EVP_EncryptUpdate");
+    int ciphertext_len = outSz;
+
+    /* Finalise the encryption. Further ciphertext bytes may be written at
+     * this stage.
+     */
+    if (1 != EVP_EncryptFinal_ex(ctx, outPtr + outSz, &outSz))
+        throw makeEVPException(0, "Failed EVP_EncryptFinal_ex");
+    ciphertext_len += outSz;
+    out.setLength(startSz+ciphertext_len); // truncate length of 'out' to final size
+    return (size32_t)ciphertext_len;
+}
+
+size32_t aesKeyDecrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const char *key, const char *iv)
+{
+    OwnedEVPCipherCtx ctx(EVP_CIPHER_CTX_new());
+    if (!ctx)
+        throw makeEVPException(0, "Failed EVP_CIPHER_CTX_new");
+
+    const size32_t cipherBlockSz = 128;
+    // from man page - "should have sufficient room for (inl + cipher_block_size) bytes unless the cipher block size is 1 in which case inl bytes is sufficient"
+    size32_t outMaxSz = (cipherBlockSz==1) ? inSz : (inSz + cipherBlockSz/8);
+    size32_t startSz = out.length();
+    byte *outPtr = (byte *)out.reserveTruncate(outMaxSz);
+
+    /* Initialise the decryption operation. IMPORTANT - ensure you use a key
+     * and IV size appropriate for your cipher
+     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
+     * IV size for *most* modes is the same as the block size. For AES this
+     * is 128 bits
+     * */
+    if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, (const unsigned char *)key, (const unsigned char *)iv))
+        throw makeEVPException(0, "Failed EVP_DecryptInit_ex");
+
+    /* Provide the message to be decrypted, and obtain the plaintext output.
+     * EVP_DecryptUpdate can be called multiple times if necessary
+     */
+    int outSz;
+    if (1 != EVP_DecryptUpdate(ctx, outPtr, &outSz, (const unsigned char *)inBytes, inSz))
+        throw makeEVPException(0, "Failed EVP_DecryptUpdate");
+    int plaintext_len = outSz;
+
+    /* Finalise the decryption. Further plaintext bytes may be written at
+     * this stage.
+     */
+    if (1 != EVP_DecryptFinal_ex(ctx, outPtr + outSz, &outSz))
+        throw makeEVPException(0, "Failed EVP_DecryptFinal_ex");
+
+    plaintext_len += outSz;
+    out.setLength(startSz+plaintext_len); // truncate length of 'out' to final size
+    return (size32_t)plaintext_len;
+}
+
+size32_t aesEncryptWithRSAEncryptedKey(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &publicKey)
+{
+    // create random AES key and IV
+    char randomAesKey[aesKeySize];
+    char randomIV[aesBlockSize];
+    fillRandomData(aesKeySize, randomAesKey);
+    fillRandomData(aesBlockSize, randomIV);
+
+    size32_t startSz = out.length();
+    DelayedSizeMarker mark(out);
+    publicKeyEncrypt(out, aesKeySize, randomAesKey, publicKey);
+    mark.write();
+    out.append(aesBlockSize, randomIV);
+
+    DelayedSizeMarker aesSz(out);
+    aesKeyEncrypt(out, inSz, inBytes, randomAesKey, randomIV);
+    aesSz.write();
+    return out.length()-startSz;
+}
+
+size32_t aesDecryptWithRSAEncryptedKey(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &privateKey)
+{
+    MemoryBuffer in;
+    in.setBuffer(inSz, (void *)inBytes, false);
+    // read encrypted AES key
+    char randomAesKey[aesKeySize];
+    size32_t encryptedAESKeySz;
+    in.read(encryptedAESKeySz);
+    MemoryBuffer aesKey;
+    size32_t decryptedAesKeySz = privateKeyDecrypt(aesKey, encryptedAESKeySz, in.readDirect(encryptedAESKeySz), privateKey);
+    if (decryptedAesKeySz != aesKeySize)
+        throw makeStringException(0, "aesDecryptWithRSAEncryptedKey - invalid input");
+
+    unsigned iVPos = in.getPos(); // read directly further down
+    in.skip(aesBlockSize);
+
+    size32_t aesEncryptedSz;
+    in.read(aesEncryptedSz);
+
+    return aesKeyDecrypt(out, aesEncryptedSz, in.readDirect(aesEncryptedSz), (const char *)aesKey.bytes(), (const char *)in.bytes()+iVPos);
+}
+
+
+} // end of namespace cryptohelper
+
+#endif // end of #if defined(_USE_OPENSSL) && !defined(_WIN32)
+

+ 56 - 0
system/security/cryptohelper/ske.hpp

@@ -0,0 +1,56 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+// Symmetric-key encryption
+
+#ifndef SKE_HPP
+#define SKE_HPP
+
+#ifndef CRYPTOHELPER_API
+
+#ifndef CRYPTOHELPER_EXPORTS
+    #define CRYPTOHELPER_API DECL_IMPORT
+#else
+    #define CRYPTOHELPER_API DECL_EXPORT
+#endif //CRYPTOHELPER_EXPORTS
+
+#endif
+
+namespace cryptohelper
+{
+
+#if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+const unsigned aesKeySize = 256/8; // 256 bits
+const unsigned aesBlockSize = 128/8; // 128 bits
+
+CRYPTOHELPER_API size32_t aesKeyEncrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const char key[aesKeySize], const char iv[aesBlockSize]);
+CRYPTOHELPER_API size32_t aesKeyDecrypt(MemoryBuffer &out, size32_t inSz, const void *inBytes, const char key[aesKeySize], const char iv[aesBlockSize]);
+
+// aesEncryptWithRSAEncryptedKey serializes encrypted data along with an RSA encrypted key in the format { RSA-encrypted-AES-key, aes-IV, AES-encrypted-data }
+CRYPTOHELPER_API size32_t aesEncryptWithRSAEncryptedKey(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &publicKey);
+// aesDecryptWithRSAEncryptedKey deserializes data created by aesEncryptWithRSAEncryptedKey
+CRYPTOHELPER_API size32_t aesDecryptWithRSAEncryptedKey(MemoryBuffer &out, size32_t inSz, const void *inBytes, const CLoadedKey &privateKey);
+
+
+#endif // end of #if defined(_USE_OPENSSL) && !defined(_WIN32)
+
+} // end of namespace cryptohelper
+
+
+#endif // SKE_HPP
+

+ 3 - 0
system/security/shared/caching.cpp

@@ -19,6 +19,9 @@
 #include "jtime.hpp"
 #include "digisign.hpp"
 
+using namespace cryptohelper;
+
+
 //define a container for multiple instances of a security manager cache
 typedef map<string, CPermissionsCache*> MapCache;
 static CriticalSection mapCacheCS;//guards modifications to the cache map

+ 3 - 3
testing/unittests/CMakeLists.txt

@@ -31,7 +31,7 @@ set (    SRCS
          remotetests.cpp
          dalitests.cpp
          jlibtests.cpp
-         digisigntests.cpp
+         cryptotests.cpp
     )
 
 include_directories (
@@ -44,7 +44,7 @@ include_directories (
          ./../../dali/base
          ./../../system/security/shared
          ./../../common/deftype
-         ./../../system/security/digisign
+         ./../../system/security/cryptohelper
     )
 
 ADD_DEFINITIONS( -D_CONSOLE )
@@ -58,7 +58,7 @@ target_link_libraries ( unittests
          dalibase
          deftype
          libbase58
-         digisign
+         cryptohelper
          ${CPPUNIT_LIBRARIES}
     )
 

+ 701 - 0
testing/unittests/cryptotests.cpp

@@ -0,0 +1,701 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+/*
+ * cryptohelper regression tests
+ *
+ */
+
+#ifdef _USE_CPPUNIT
+
+#include <functional>
+
+#include "jencrypt.hpp"
+
+#include "unittests.hpp"
+#include "digisign.hpp"
+#include "pke.hpp"
+#include "ske.hpp"
+
+
+/* ============================================================= */
+
+const char * privKey =
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIIEpAIBAAKCAQEA1SISNsSwg81TClnjGtVV61cYOmGWgKbbnpvSOqbZWMHs9W4L\n"
+"g2waNbGnQ3G+l7alWiMRV4qTjhkrdVRuvTxLOGOAfAhGB4Y5txDUNdTwuSp+Gpuq\n"
+"coYOmQWW4IIrjIOZMlamZQcIs7p/2CzfoLQHNuFuBeR+MLDMsMO7O42N+pcFWqPC\n"
+"plmRIkDWB2ru+DpJcaTtts/16f1/nf4KbMmpVlObWP/l48/XZjythzQir5AV6W13\n"
+"VM7MFToQqPxjy/c9F06/RjiW7sFv/r58pNsPk0iWcL0wBJ0GHZRGsCNOKMl08qow\n"
+"jynIVMKhIADYFXm84r69R1CO9KocixnqsH29uQIDAQABAoIBAEMCdFGN46V837fo\n"
+"bPPZ0Sqt9msclZIbY/9pJF7WaI10Y0kC8VG/ojnxghI9Z9wRS8mcLu6kHiJWHYjF\n"
+"JBARLeErv5C/lSz2cZzyCJZoPcsp5f39pUheh6Zq0HYD1ydVlMvz3Fr1LDI918Yi\n"
+"zaicEYyasdnebiJm4+RLlclyhwoa8CeRNLbLRoHcL7mu7sHHDMIWS86P/axfAnZ4\n"
+"yk2DKqjFflgd1zmRW5JLj6phb80ehuFIMJQ/Llwm4LY3uvg11D8c8ZDXQnMVSAIE\n"
+"fV5X9dtS1LCexYIpRmLj/LTAYZbQSdmE2w2lXLnDewiFD57eJNjYK9O0+iZg/Nfj\n"
+"i/95tVUCgYEA+D7N/LmWil3n/jz6zmrZPj+j7fjiZ+YiJ1mIOROvnlHhOgGzzjV5\n"
+"hFAVET9vlqSQoOelK9aYVEl9yfi9fRq1TUGLucS9+x5Urt1FBWyJw5cgdkpUIY4k\n"
+"pa9CCvnKrOieL+Rs4mU6XKqwx8iswv5PeOzW6/aMwbloVsAkYxEFiRsCgYEA28p6\n"
+"JDMO0pJE3rmesyxpLMayGCtpiFhbhuoIsveae2Hf6Qe7Bg73FEMHeDnjgXzpN8IY\n"
+"YgAMXglRsN09lPRc7cxUWdsr0slu8J/ouCaYu7l0i+Y1fp3YWLnUp56T45GGJPEI\n"
+"ro6EIhyX2J7abFV5qNHzI+AnlPubL8XCzaUwNbsCgYEA2F/NpYGSAIq3Ynd+WJrz\n"
+"Pfm0hgDQPqVtkYTNYoqRIVrXCHthYNRlVXmD02PKfLB1y3n9EsfaQGVKOdgQOdIk\n"
+"wvDlvAcLXK1kPIJq3b5sGcpJJjHFQPYnZS7sTqrJCIs9Dht4+KApDYpNyeVVCCUn\n"
+"2gv9jPB6YYScuDiDvsGgZI8CgYBUg1bT9I4Oig/RVK6hVsJaZUy13nuF4fPPvM37\n"
+"gxnzt37RrBdODRMUx3Fn2VqRv+YteoTFqh8XSZ4P1AKJ9CyHg7orkwsW0j3GaLaj\n"
+"mLPB+13FLZAET82Q0GPk0CUtrBdYvRYJiONl+nio4uw6G+Pb9l73vIl70AOsKu7t\n"
+"BEe1YQKBgQDeW3xAP3ceoJAW5qfnTzCY20wfs/epVSczqm2ZBrLhnY2l0FgefXCg\n"
+"eLLmQ8M0zCPrEIxsz11QmGunOVE0/boEOShYbOVTjAEES9a/+FHg0gyIP4zU8WZc\n"
+"dye0XkCOCkcGIwdas3fwf+O0lwZc+dvPdKVak+e8qL9aPgiQCb2/ww==\n"
+"-----END RSA PRIVATE KEY-----\n";
+
+const char * pubKey =
+"-----BEGIN PUBLIC KEY-----\n"
+"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1SISNsSwg81TClnjGtVV\n"
+"61cYOmGWgKbbnpvSOqbZWMHs9W4Lg2waNbGnQ3G+l7alWiMRV4qTjhkrdVRuvTxL\n"
+"OGOAfAhGB4Y5txDUNdTwuSp+GpuqcoYOmQWW4IIrjIOZMlamZQcIs7p/2CzfoLQH\n"
+"NuFuBeR+MLDMsMO7O42N+pcFWqPCplmRIkDWB2ru+DpJcaTtts/16f1/nf4KbMmp\n"
+"VlObWP/l48/XZjythzQir5AV6W13VM7MFToQqPxjy/c9F06/RjiW7sFv/r58pNsP\n"
+"k0iWcL0wBJ0GHZRGsCNOKMl08qowjynIVMKhIADYFXm84r69R1CO9KocixnqsH29\n"
+"uQIDAQAB\n"
+"-----END PUBLIC KEY-----\n";
+
+/* ============================================================= */
+
+
+using namespace cryptohelper;
+
+#ifdef _USE_OPENSSL
+class CryptoUnitTest : public CppUnit::TestFixture
+{
+public:
+    CPPUNIT_TEST_SUITE(CryptoUnitTest);
+        CPPUNIT_TEST(digiSignTests);
+        CPPUNIT_TEST(pkeEncryptDecryptTest);
+        CPPUNIT_TEST(pkeParallelTest);
+        CPPUNIT_TEST(aesEncryptDecryptTests);
+        CPPUNIT_TEST(aesWithRsaEncryptedKey);
+        CPPUNIT_TEST(aesParallelTest);
+    CPPUNIT_TEST_SUITE_END();
+
+protected:
+    void asyncDigiSignUnitTest(IDigitalSignatureManager * _dsm)
+    {
+
+        class casyncfor: public CAsyncFor
+        {
+            IDigitalSignatureManager * dsm;
+        public:
+            casyncfor(IDigitalSignatureManager * _dsm)
+            {
+                dsm = _dsm;
+            }
+            void Do(unsigned idx)
+            {
+                VStringBuffer text("I am here %d", idx);
+                StringBuffer sig;
+                bool ok = dsm->digiSign(text, sig);
+                if (!ok)
+                    printf("Asynchronous asyncDigiSignUnitTest() test %d failed!\n", idx);
+                ASSERT(ok);
+            }
+        } afor(_dsm);
+
+        printf("Executing 1000 asyncDigiSignUnitTest() operations\n");
+        afor.For(1000,20,true,true);
+        printf("Asynchronous asyncDigiSignUnitTest() test complete\n");
+    }
+
+    void asyncDigiVerifyUnitTest(IDigitalSignatureManager * _dsm)
+    {
+
+        class casyncfor: public CAsyncFor
+        {
+            IDigitalSignatureManager * dsm;
+            StringBuffer text;
+            StringBuffer sig;
+        public:
+            casyncfor(IDigitalSignatureManager * _dsm)
+            {
+                dsm = _dsm;
+                text.set("I am here");
+                bool ok = dsm->digiSign(text, sig);
+                if (!ok)
+                    printf("Asynchronous asyncDigiVerifyUnitTest() failed in digiSign!\n");
+                ASSERT(ok);
+            }
+            void Do(unsigned idx)
+            {
+                bool ok = dsm->digiVerify(text, sig);
+                if (!ok)
+                    printf("Asynchronous asyncDigiVerifyUnitTest() test %d failed!\n", idx);
+                ASSERT(ok);
+            }
+        } afor(_dsm);
+
+        printf("Executing 1000 asyncDigiVerifyUnitTest() operations\n");
+        afor.For(1000,20,true,true);
+        printf("Asynchronous asyncDigiVerifyUnitTest() test complete\n");
+    }
+
+    void asyncDigiSignAndVerifyUnitTest(IDigitalSignatureManager * _dsm)
+    {
+
+        class casyncfor: public CAsyncFor
+        {
+            IDigitalSignatureManager * dsm;
+        public:
+            casyncfor(IDigitalSignatureManager * _dsm)
+            {
+                dsm = _dsm;
+            }
+            void Do(unsigned idx)
+            {
+                VStringBuffer text("I am here %d", idx);
+                StringBuffer sig;
+                bool ok = dsm->digiSign(text, sig);
+                if (!ok)
+                    printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test %d failed!\n", idx);
+                ASSERT(ok);
+
+                ok = dsm->digiVerify(text, sig);
+                if (!ok)
+                    printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test %d failed!\n", idx);
+                ASSERT(ok);
+            }
+        } afor(_dsm);
+
+        printf("Executing 1000 asynchronous asyncDigiSignAndVerifyUnitTest() operations\n");
+        afor.For(1000,20,true,true);
+        printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test complete\n");
+    }
+
+    void digiSignTests()
+    {
+        Owned<IException> exception;
+        CppUnit::Exception *cppunitException;
+
+        const char * text1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        const char * text2 = "~`!@#$%^&*()_-+=0123456789{[}]:;\"'<,>.?/'";
+        const char * text3 = "W20180301-154415;ECLUsername";
+        StringBuffer sig1;
+        StringBuffer sig2;
+        StringBuffer sig3;
+
+        try
+        {
+            printf("\nExecuting digiSign() unit tests\n");
+
+            //Create instance of digital signature manager
+            StringBuffer _pubKeyBuff(pubKey);
+            StringBuffer _privKeyBuff(privKey);
+            Owned<IDigitalSignatureManager> dsm(createDigitalSignatureManagerInstanceFromKeys(_pubKeyBuff, _privKeyBuff, nullptr));
+
+
+            printf("digiSign() test 1\n");
+            StringBuffer txt(text1);
+            bool ok = dsm->digiSign(text1, sig1.clear());
+            ASSERT(ok);
+            ASSERT(0 == strcmp(text1, txt.str()));//source string should be unchanged
+            ASSERT(!sig1.isEmpty());//signature should be populated
+
+            StringBuffer sig(sig1);
+            ok = dsm->digiVerify(text1, sig1);
+            ASSERT(ok);
+            ASSERT(0 == strcmp(text1, txt.str()));//source string should be unchanged
+            ASSERT(0 == strcmp(sig.str(), sig1.str()));//signature should be unchanged
+
+            printf("digiSign() test 2\n");
+            ok = dsm->digiVerify(text1, sig1);
+            ASSERT(ok);
+            ok = dsm->digiVerify(text1, sig1);
+            ASSERT(ok);
+
+            printf("digiSign() test 3\n");
+            ok = dsm->digiSign(text2, sig2.clear());
+            ASSERT(ok);
+            ok = dsm->digiVerify(text2, sig2);
+            ASSERT(ok);
+            ok = dsm->digiSign(text2, sig2.clear());
+            ASSERT(ok);
+            ok = dsm->digiVerify(text2, sig2);
+            ASSERT(ok);
+
+            printf("digiSign() test 4\n");
+            ok = dsm->digiVerify(text1, sig1);
+            ASSERT(ok);
+
+            printf("digiSign() test 5\n");
+            ok = dsm->digiVerify(text2, sig2);
+            ASSERT(ok);
+
+            printf("digiSign() test 6\n");
+            ok = dsm->digiVerify(text1, sig2);
+            ASSERT(!ok);//should fail
+
+            printf("digiSign() test 7\n");
+            ok = dsm->digiVerify(text2, sig1);
+            ASSERT(!ok);//should fail
+
+            printf("digiSign() test 8\n");
+            ok = dsm->digiSign(text3, sig3.clear());
+            ASSERT(ok);
+
+            printf("digiSign() test 9\n");
+            ok = dsm->digiVerify(text3, sig1);
+            ASSERT(!ok);//should fail
+            ok = dsm->digiVerify(text3, sig2);
+            ASSERT(!ok);//should fail
+            ok = dsm->digiVerify(text3, sig3);
+            ASSERT(ok);
+
+            //Perform
+            printf("digiSign() loop test\n");
+            unsigned now = msTick();
+            for (int x=0; x<1000; x++)
+            {
+                dsm->digiSign(text3, sig3.clear());
+            }
+            printf("digiSign() 1000 iterations took %d MS\n", msTick() - now);
+
+            printf("digiVerify() loop test\n");
+            now = msTick();
+            for (int x=0; x<1000; x++)
+            {
+                dsm->digiVerify(text3, sig3);
+            }
+            printf("digiverify 1000 iterations took %d MS\n", msTick() - now);
+
+            now = msTick();
+            printf("\nAsynchronous test digiSign\n");
+            asyncDigiSignUnitTest(dsm);
+            printf("digiSign 1000 async iterations took %d MS\n", msTick() - now);
+
+            now = msTick();
+            printf("\nAsynchronous test digiVerify\n");
+            asyncDigiVerifyUnitTest(dsm);
+            printf("digiverify 1000 async iterations took %d MS\n", msTick() - now);
+
+            now = msTick();
+            printf("\nAsynchronous test digiSign and digiVerify\n");
+            asyncDigiSignAndVerifyUnitTest(dsm);
+            printf("digiSign/digiverify 1000 async iterations took %d MS\n", msTick() - now);
+        }
+        catch (IException *e)
+        {
+            StringBuffer err;
+            e->errorMessage(err);
+            printf("Digisign IException thrown:%s\n", err.str());
+            exception.setown(e);
+        }
+        catch (CppUnit::Exception &e)
+        {
+            printf("Digisign CppUnit::Exception thrown\n");
+            cppunitException = e.clone();
+        }
+        printf("Completed executing digiSign() unit tests\n");
+    }
+
+    void _pkeEncryptDecryptTest()
+    {
+        try
+        {
+            Owned<CLoadedKey> publicKey = loadPublicKeyFromMemory(pubKey, nullptr);
+            Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privKey, nullptr);
+
+            // create random data
+            MemoryBuffer toEncryptMb;
+            fillRandomData(245, toEncryptMb); // max for RSA
+
+            MemoryBuffer pkeMb;
+            publicKeyEncrypt(pkeMb, toEncryptMb.length(), toEncryptMb.bytes(), *publicKey);
+
+            MemoryBuffer decryptedMb;
+            privateKeyDecrypt(decryptedMb, pkeMb.length(), pkeMb.bytes(), *privateKey);
+
+            ASSERT(toEncryptMb.length() == decryptedMb.length());
+            ASSERT(0 == memcmp(toEncryptMb.bytes(), decryptedMb.bytes(), toEncryptMb.length()));
+        }
+        catch (IException *e)
+        {
+            StringBuffer err;
+            e->errorMessage(err);
+            printf("pkeEncryptDecryptTest IException thrown:%s\n", err.str());
+            throw;
+        }
+        catch (CppUnit::Exception &e)
+        {
+            printf("pkeEncryptDecryptTest CppUnit::Exception thrown\n");
+            throw;
+        }
+    }
+
+    void pkeEncryptDecryptTest()
+    {
+        printf("\nExecuting pkeEncryptDecryptTest() unit tests\n");
+        _pkeEncryptDecryptTest();
+    }
+
+
+    void pkeParallelTest()
+    {
+        class CAsyncfor : public CAsyncFor
+        {
+            std::function<void()> testFunc;
+        public:
+            CAsyncfor(std::function<void()> _testFunc) : testFunc(_testFunc)
+            {
+            }
+            void Do(unsigned idx)
+            {
+                testFunc();
+            }
+        } afor(std::bind(&CryptoUnitTest::_pkeEncryptDecryptTest, this));
+
+        printf("\nExecuting 1000 asynchronous pkeParallelTest() operations\n");
+        CCycleTimer timer;
+        afor.For(1000, 20, true, true);
+        printf("Asynchronous pkeParallelTest() test completed in %u ms\n", timer.elapsedMs());
+    }
+
+    void aesEncryptDecryptTests()
+    {
+        try
+        {
+            printf("\nExecuting aesEncryptDecryptTests() unit tests\n");
+            // create random data
+            MemoryBuffer messageMb, encryptedMessageMb, decryptedMessageMb;
+
+            char aesKey[aesKeySize];
+            char aesIV[aesBlockSize];
+            fillRandomData(aesKeySize, aesKey);
+            fillRandomData(aesBlockSize, aesIV);
+
+            fillRandomData(1024*100, messageMb);
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb, messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb, encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+            messageMb.clear(); // 0 length test
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb.clear(), messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb.clear(), encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+            fillRandomData(1, messageMb.clear()); // 1 byte test
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb.clear(), messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb.clear(), encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+            fillRandomData(cryptohelper::aesBlockSize-1, messageMb.clear()); // aesBlockSize-1 test
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb.clear(), messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb.clear(), encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+            fillRandomData(cryptohelper::aesBlockSize, messageMb.clear()); // aesBlockSize test
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb.clear(), messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb.clear(), encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+            fillRandomData(cryptohelper::aesBlockSize+1, messageMb.clear()); // aesBlockSize+1 test
+            printf("aesEncryptDecryptTests with %u bytes\n", messageMb.length());
+            aesKeyEncrypt(encryptedMessageMb.clear(), messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+            aesKeyDecrypt(decryptedMessageMb.clear(), encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+        }
+        catch (IException *e)
+        {
+            StringBuffer err;
+            e->errorMessage(err);
+            printf("aesWithRsaEncryptedKey IException thrown:%s\n", err.str());
+            throw;
+        }
+        catch (CppUnit::Exception &e)
+        {
+            printf("aesWithRsaEncryptedKey CppUnit::Exception thrown\n");
+            throw;
+        }
+    }
+
+    void aesWithRsaEncryptedKey()
+    {
+        try
+        {
+            printf("\nExecuting aesWithRsaEncryptedKey() unit tests\n");
+            // create random data
+            MemoryBuffer messageMb;
+            fillRandomData(1024*100, messageMb);
+
+            char aesKey[aesKeySize];
+            char aesIV[aesBlockSize];
+            fillRandomData(aesKeySize, aesKey);
+            fillRandomData(aesBlockSize, aesIV);
+
+            Owned<CLoadedKey> publicKey = loadPublicKeyFromMemory(pubKey, nullptr);
+            MemoryBuffer encryptedMessageMb;
+            aesEncryptWithRSAEncryptedKey(encryptedMessageMb, messageMb.length(), messageMb.bytes(), *publicKey);
+
+            // would normally be server side
+            Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privKey, nullptr);
+            MemoryBuffer decryptedMessageMb;
+            aesDecryptWithRSAEncryptedKey(decryptedMessageMb, encryptedMessageMb.length(), encryptedMessageMb.bytes(), *privateKey);
+
+            ASSERT(messageMb.length() == decryptedMessageMb.length());
+            ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+        }
+        catch (IException *e)
+        {
+            StringBuffer err;
+            e->errorMessage(err);
+            printf("aesWithRsaEncryptedKey IException thrown:%s\n", err.str());
+            throw;
+        }
+        catch (CppUnit::Exception &e)
+        {
+            printf("aesWithRsaEncryptedKey CppUnit::Exception thrown\n");
+            throw;
+        }
+    }
+
+    void aesParallelTest()
+    {
+        class CAsyncfor : public CAsyncFor
+        {
+            MemoryBuffer messageMb;
+            char aesKey[aesKeySize];
+            char aesIV[aesBlockSize];
+        public:
+            CAsyncfor()
+            {
+                // create random key
+                fillRandomData(aesKeySize, aesKey);
+                fillRandomData(aesBlockSize, aesIV);
+                // create random data
+                fillRandomData(1024*100, messageMb);
+            }
+            void Do(unsigned idx)
+            {
+                MemoryBuffer encryptedMessageMb;
+                aesKeyEncrypt(encryptedMessageMb, messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+
+                MemoryBuffer decryptedMessageMb;
+                aesKeyDecrypt(decryptedMessageMb, encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+
+                ASSERT(messageMb.length() == decryptedMessageMb.length());
+                ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+            }
+        } afor;
+
+        printf("\nExecuting 1000 asynchronous aesParallelTest() operations\n");
+        CCycleTimer timer;
+        afor.For(1000, 20, true, true);
+        printf("Asynchronous aesParallelTest() test completed in %u ms\n", timer.elapsedMs());
+    }
+};
+
+class CryptoTestTiming : public CppUnit::TestFixture
+{
+    size32_t dataSz = 0x100000 * 10; // 10MB
+
+public:
+    CPPUNIT_TEST_SUITE(CryptoTestTiming);
+        CPPUNIT_TEST(aesSpeedTest);
+        CPPUNIT_TEST(rsaSpeedTest);
+        CPPUNIT_TEST(rsaKeyLoadSpeedTest);
+        CPPUNIT_TEST(aesCompareJlibVsCryptoHelper);
+    CPPUNIT_TEST_SUITE_END();
+
+    void aesCompareJlibVsCryptoHelper()
+    {
+        MemoryBuffer messageMb, encryptedMessageMb, decryptedMessageMb;
+        char aesKey[aesKeySize];
+        char aesIV[aesBlockSize];
+        // create random key
+        fillRandomData(aesKeySize, aesKey);
+        fillRandomData(aesBlockSize, aesIV);
+
+        // create random data
+        fillRandomData(dataSz, messageMb);
+
+        encryptedMessageMb.ensureCapacity(dataSz+aesBlockSize);
+
+        CCycleTimer timer;
+        aesKeyEncrypt(encryptedMessageMb, messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+        printf("OPENSSL AES %u MB encrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+
+        decryptedMessageMb.ensureCapacity(encryptedMessageMb.length()+aesBlockSize);
+        timer.reset();
+        aesKeyDecrypt(decryptedMessageMb, encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+        printf("OPENSSL AES %u MB decrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+
+        ASSERT(messageMb.length() == decryptedMessageMb.length());
+        ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+
+        encryptedMessageMb.clear();
+        timer.reset();
+        aesEncrypt(aesKey, aesKeySize, messageMb.bytes(), messageMb.length(), encryptedMessageMb);
+        printf("JLIB    AES %u MB encrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+
+        decryptedMessageMb.clear();
+        timer.reset();
+        aesDecrypt(aesKey, aesKeySize, encryptedMessageMb.bytes(), encryptedMessageMb.length(), decryptedMessageMb);
+        printf("JLIB    AES %u MB decrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+
+        ASSERT(messageMb.length() == decryptedMessageMb.length());
+        ASSERT(0 == memcmp(messageMb.bytes(), decryptedMessageMb.bytes(), messageMb.length()));
+    }
+
+    void aesSpeedTest()
+    {
+        MemoryBuffer messageMb;
+        char aesKey[aesKeySize];
+        char aesIV[aesBlockSize];
+        // create random key
+        fillRandomData(aesKeySize, aesKey);
+        fillRandomData(aesBlockSize, aesIV);
+
+        // create random data
+        fillRandomData(dataSz, messageMb);
+
+        MemoryBuffer encryptedMessageMb;
+        encryptedMessageMb.ensureCapacity(dataSz+aesBlockSize);
+        CCycleTimer timer;
+        aesKeyEncrypt(encryptedMessageMb, messageMb.length(), messageMb.bytes(), aesKey, aesIV);
+        printf("AES %u MB encrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+        MemoryBuffer decryptedMessageMb;
+        decryptedMessageMb.ensureCapacity(encryptedMessageMb.length()+aesBlockSize);
+        timer.reset();
+        aesKeyDecrypt(decryptedMessageMb, encryptedMessageMb.length(), encryptedMessageMb.bytes(), aesKey, aesIV);
+        printf("AES %u MB decrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+    }
+
+    void rsaSpeedTest()
+    {
+        // create random data
+        MemoryBuffer messageMb;
+        fillRandomData(dataSz, messageMb);
+
+        Owned<CLoadedKey> publicKey = loadPublicKeyFromMemory(pubKey, nullptr);
+        Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privKey, nullptr);
+
+        MemoryBuffer encryptedMessageMb;
+        MemoryBuffer decryptedMessageMb;
+        // pre-alloc memory, so as not part of the timing
+        size32_t maxPerEncryptSz = 245;
+        unsigned numPackets = ((dataSz + (maxPerEncryptSz-1)) / maxPerEncryptSz);
+        size32_t dstMaxSz = numPackets * 256; // approx
+        encryptedMessageMb.ensureCapacity(dstMaxSz);
+
+        const byte *src = messageMb.bytes();
+        byte *dst = (byte *)encryptedMessageMb.bufferBase();
+        size32_t remaining = dataSz;
+        size32_t encryptPacketSz = 256;
+        CCycleTimer timer;
+        while (true)
+        {
+            size32_t cp = remaining>maxPerEncryptSz ? maxPerEncryptSz : remaining;
+            size_t eSz = publicKeyEncrypt(dst, dstMaxSz, cp, src, *publicKey);
+            assertex(eSz);
+            assertex(eSz == encryptPacketSz); //consistent for size being encrypted, assumed on decrypt
+            src += cp;
+            remaining -= cp;
+            dst += eSz;
+            dstMaxSz -= eSz;
+            if (0 == remaining)
+                break;
+        }
+        encryptedMessageMb.rewrite(dst-(byte*)encryptedMessageMb.bufferBase());
+        printf("RSA %u MB encrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+
+        size32_t encryptedDataSz = encryptedMessageMb.length();
+        remaining = encryptedDataSz;
+        src = encryptedMessageMb.bytes();
+        dstMaxSz = ((numPackets-1) * maxPerEncryptSz) + encryptPacketSz; // because encrypt always needs buffer to have enough room for encryptPacketSz
+        decryptedMessageMb.ensureCapacity(dstMaxSz);
+        dst = (byte *)decryptedMessageMb.bufferBase();
+        timer.reset();
+        while (true)
+        {
+            size_t eSz = privateKeyDecrypt(dst, dstMaxSz, encryptPacketSz, src, *privateKey);
+            assertex(eSz);
+            assertex(eSz <= maxPerEncryptSz);
+            src += encryptPacketSz;
+            remaining -= encryptPacketSz;
+            dst += eSz;
+            dstMaxSz -= eSz;
+            if (0 == remaining)
+                break;
+        }
+        printf("RSA %u MB decrypt time: %u ms\n", dataSz/0x100000, timer.elapsedMs());
+    }
+
+    void rsaKeyLoadSpeedTest()
+    {
+        // create random data
+        size32_t dataSz = 245;
+        MemoryBuffer messageMb;
+        fillRandomData(dataSz, messageMb);
+
+        unsigned numCycles = 1000;
+        CCycleTimer timer;
+        for (unsigned i=0; i<numCycles; i++)
+        {
+            Owned<CLoadedKey> publicKey = loadPublicKeyFromMemory(pubKey, nullptr);
+            Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privKey, nullptr);
+
+            MemoryBuffer pkeMb;
+            publicKeyEncrypt(pkeMb, messageMb.length(), messageMb.bytes(), *publicKey);
+
+            MemoryBuffer decryptedMb;
+            privateKeyDecrypt(decryptedMb, pkeMb.length(), pkeMb.bytes(), *privateKey);
+        }
+        printf("RSA %u cycles - reloading keys each iteration - %u ms\n", numCycles, timer.elapsedMs());
+
+        Owned<CLoadedKey> publicKey = loadPublicKeyFromMemory(pubKey, nullptr);
+        Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privKey, nullptr);
+
+        timer.reset();
+        for (unsigned i=0; i<numCycles; i++)
+        {
+            MemoryBuffer pkeMb;
+            publicKeyEncrypt(pkeMb, messageMb.length(), messageMb.bytes(), *publicKey);
+
+            MemoryBuffer decryptedMb;
+            privateKeyDecrypt(decryptedMb, pkeMb.length(), pkeMb.bytes(), *privateKey);
+        }
+        printf("RSA %u cycles - reusing loaded keys - %u ms\n", numCycles, timer.elapsedMs());
+    }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( CryptoUnitTest );
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( CryptoUnitTest, "CryptoUnitTest" );
+CPPUNIT_TEST_SUITE_REGISTRATION( CryptoTestTiming );
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( CryptoTestTiming, "CryptoTestTiming" );
+
+#endif
+
+#endif // _USE_CPPUNIT

+ 0 - 302
testing/unittests/digisigntests.cpp

@@ -1,302 +0,0 @@
-/*##############################################################################
-
-    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-############################################################################## */
-
-/*
- * digisign regression tests
- *
- */
-
-#ifdef _USE_CPPUNIT
-
-#include "unittests.hpp"
-#include "digisign.hpp"
-
-/* ============================================================= */
-
-const char * privKey =
-"-----BEGIN RSA PRIVATE KEY-----\n"
-"MIIEpAIBAAKCAQEA1SISNsSwg81TClnjGtVV61cYOmGWgKbbnpvSOqbZWMHs9W4L\n"
-"g2waNbGnQ3G+l7alWiMRV4qTjhkrdVRuvTxLOGOAfAhGB4Y5txDUNdTwuSp+Gpuq\n"
-"coYOmQWW4IIrjIOZMlamZQcIs7p/2CzfoLQHNuFuBeR+MLDMsMO7O42N+pcFWqPC\n"
-"plmRIkDWB2ru+DpJcaTtts/16f1/nf4KbMmpVlObWP/l48/XZjythzQir5AV6W13\n"
-"VM7MFToQqPxjy/c9F06/RjiW7sFv/r58pNsPk0iWcL0wBJ0GHZRGsCNOKMl08qow\n"
-"jynIVMKhIADYFXm84r69R1CO9KocixnqsH29uQIDAQABAoIBAEMCdFGN46V837fo\n"
-"bPPZ0Sqt9msclZIbY/9pJF7WaI10Y0kC8VG/ojnxghI9Z9wRS8mcLu6kHiJWHYjF\n"
-"JBARLeErv5C/lSz2cZzyCJZoPcsp5f39pUheh6Zq0HYD1ydVlMvz3Fr1LDI918Yi\n"
-"zaicEYyasdnebiJm4+RLlclyhwoa8CeRNLbLRoHcL7mu7sHHDMIWS86P/axfAnZ4\n"
-"yk2DKqjFflgd1zmRW5JLj6phb80ehuFIMJQ/Llwm4LY3uvg11D8c8ZDXQnMVSAIE\n"
-"fV5X9dtS1LCexYIpRmLj/LTAYZbQSdmE2w2lXLnDewiFD57eJNjYK9O0+iZg/Nfj\n"
-"i/95tVUCgYEA+D7N/LmWil3n/jz6zmrZPj+j7fjiZ+YiJ1mIOROvnlHhOgGzzjV5\n"
-"hFAVET9vlqSQoOelK9aYVEl9yfi9fRq1TUGLucS9+x5Urt1FBWyJw5cgdkpUIY4k\n"
-"pa9CCvnKrOieL+Rs4mU6XKqwx8iswv5PeOzW6/aMwbloVsAkYxEFiRsCgYEA28p6\n"
-"JDMO0pJE3rmesyxpLMayGCtpiFhbhuoIsveae2Hf6Qe7Bg73FEMHeDnjgXzpN8IY\n"
-"YgAMXglRsN09lPRc7cxUWdsr0slu8J/ouCaYu7l0i+Y1fp3YWLnUp56T45GGJPEI\n"
-"ro6EIhyX2J7abFV5qNHzI+AnlPubL8XCzaUwNbsCgYEA2F/NpYGSAIq3Ynd+WJrz\n"
-"Pfm0hgDQPqVtkYTNYoqRIVrXCHthYNRlVXmD02PKfLB1y3n9EsfaQGVKOdgQOdIk\n"
-"wvDlvAcLXK1kPIJq3b5sGcpJJjHFQPYnZS7sTqrJCIs9Dht4+KApDYpNyeVVCCUn\n"
-"2gv9jPB6YYScuDiDvsGgZI8CgYBUg1bT9I4Oig/RVK6hVsJaZUy13nuF4fPPvM37\n"
-"gxnzt37RrBdODRMUx3Fn2VqRv+YteoTFqh8XSZ4P1AKJ9CyHg7orkwsW0j3GaLaj\n"
-"mLPB+13FLZAET82Q0GPk0CUtrBdYvRYJiONl+nio4uw6G+Pb9l73vIl70AOsKu7t\n"
-"BEe1YQKBgQDeW3xAP3ceoJAW5qfnTzCY20wfs/epVSczqm2ZBrLhnY2l0FgefXCg\n"
-"eLLmQ8M0zCPrEIxsz11QmGunOVE0/boEOShYbOVTjAEES9a/+FHg0gyIP4zU8WZc\n"
-"dye0XkCOCkcGIwdas3fwf+O0lwZc+dvPdKVak+e8qL9aPgiQCb2/ww==\n"
-"-----END RSA PRIVATE KEY-----\n";
-
-const char * pubKey =
-"-----BEGIN PUBLIC KEY-----\n"
-"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1SISNsSwg81TClnjGtVV\n"
-"61cYOmGWgKbbnpvSOqbZWMHs9W4Lg2waNbGnQ3G+l7alWiMRV4qTjhkrdVRuvTxL\n"
-"OGOAfAhGB4Y5txDUNdTwuSp+GpuqcoYOmQWW4IIrjIOZMlamZQcIs7p/2CzfoLQH\n"
-"NuFuBeR+MLDMsMO7O42N+pcFWqPCplmRIkDWB2ru+DpJcaTtts/16f1/nf4KbMmp\n"
-"VlObWP/l48/XZjythzQir5AV6W13VM7MFToQqPxjy/c9F06/RjiW7sFv/r58pNsP\n"
-"k0iWcL0wBJ0GHZRGsCNOKMl08qowjynIVMKhIADYFXm84r69R1CO9KocixnqsH29\n"
-"uQIDAQAB\n"
-"-----END PUBLIC KEY-----\n";
-
-/* ============================================================= */
-
-class DigiSignUnitTest : public CppUnit::TestFixture
-{
-public:
-    CPPUNIT_TEST_SUITE(DigiSignUnitTest);
-        CPPUNIT_TEST(testSimple);
-    CPPUNIT_TEST_SUITE_END();
-
-protected:
-    void asyncDigiSignUnitTest(IDigitalSignatureManager * _dsm)
-    {
-
-        class casyncfor: public CAsyncFor
-        {
-            IDigitalSignatureManager * dsm;
-        public:
-            casyncfor(IDigitalSignatureManager * _dsm)
-            {
-                dsm = _dsm;
-            }
-            void Do(unsigned idx)
-            {
-                VStringBuffer text("I am here %d", idx);
-                StringBuffer sig;
-                bool ok = dsm->digiSign(text, sig);
-                if (!ok)
-                    printf("Asynchronous asyncDigiSignUnitTest() test %d failed!\n", idx);
-                ASSERT(ok);
-            }
-        } afor(_dsm);
-
-        printf("Executing 1000 asyncDigiSignUnitTest() operations\n");
-        afor.For(1000,20,true,true);
-        printf("Asynchronous asyncDigiSignUnitTest() test complete\n");
-    }
-
-    void asyncDigiVerifyUnitTest(IDigitalSignatureManager * _dsm)
-    {
-
-        class casyncfor: public CAsyncFor
-        {
-            IDigitalSignatureManager * dsm;
-            StringBuffer text;
-            StringBuffer sig;
-        public:
-            casyncfor(IDigitalSignatureManager * _dsm)
-            {
-                dsm = _dsm;
-                text.set("I am here");
-                bool ok = dsm->digiSign(text, sig);
-                if (!ok)
-                    printf("Asynchronous asyncDigiVerifyUnitTest() failed in digiSign!\n");
-                ASSERT(ok);
-            }
-            void Do(unsigned idx)
-            {
-                bool ok = dsm->digiVerify(text, sig);
-                if (!ok)
-                    printf("Asynchronous asyncDigiVerifyUnitTest() test %d failed!\n", idx);
-                ASSERT(ok);
-            }
-        } afor(_dsm);
-
-        printf("Executing 1000 asyncDigiVerifyUnitTest() operations\n");
-        afor.For(1000,20,true,true);
-        printf("Asynchronous asyncDigiVerifyUnitTest() test complete\n");
-    }
-
-    void asyncDigiSignAndVerifyUnitTest(IDigitalSignatureManager * _dsm)
-    {
-
-        class casyncfor: public CAsyncFor
-        {
-            IDigitalSignatureManager * dsm;
-        public:
-            casyncfor(IDigitalSignatureManager * _dsm)
-            {
-                dsm = _dsm;
-            }
-            void Do(unsigned idx)
-            {
-                VStringBuffer text("I am here %d", idx);
-                StringBuffer sig;
-                bool ok = dsm->digiSign(text, sig);
-                if (!ok)
-                    printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test %d failed!\n", idx);
-                ASSERT(ok);
-
-                ok = dsm->digiVerify(text, sig);
-                if (!ok)
-                    printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test %d failed!\n", idx);
-                ASSERT(ok);
-            }
-        } afor(_dsm);
-
-        printf("Executing 1000 asynchronous asyncDigiSignAndVerifyUnitTest() operations\n");
-        afor.For(1000,20,true,true);
-        printf("Asynchronous asyncDigiSignAndVerifyUnitTest() test complete\n");
-}
-
-    void testSimple()
-    {
-        Owned<IException> exception;
-        CppUnit::Exception *cppunitException;
-
-        const char * text1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-        const char * text2 = "~`!@#$%^&*()_-+=0123456789{[}]:;\"'<,>.?/'";
-        const char * text3 = "W20180301-154415;ECLUsername";
-        StringBuffer sig1;
-        StringBuffer sig2;
-        StringBuffer sig3;
-
-        try
-        {
-            printf("\nExecuting digiSign() unit tests\n");
-
-            //Create instance of digital signature manager
-            StringBuffer _pubKeyBuff(pubKey);
-            StringBuffer _privKeyBuff(privKey);
-            Owned<IDigitalSignatureManager> dsm(createDigitalSignatureManagerInstanceFromKeys(_pubKeyBuff, _privKeyBuff, nullptr));
-
-
-            printf("digiSign() test 1\n");
-            StringBuffer txt(text1);
-            bool ok = dsm->digiSign(text1, sig1.clear());
-            ASSERT(ok);
-            ASSERT(0 == strcmp(text1, txt.str()));//source string should be unchanged
-            ASSERT(!sig1.isEmpty());//signature should be populated
-
-            StringBuffer sig(sig1);
-            ok = dsm->digiVerify(text1, sig1);
-            ASSERT(ok);
-            ASSERT(0 == strcmp(text1, txt.str()));//source string should be unchanged
-            ASSERT(0 == strcmp(sig.str(), sig1.str()));//signature should be unchanged
-
-            printf("digiSign() test 2\n");
-            ok = dsm->digiVerify(text1, sig1);
-            ASSERT(ok);
-            ok = dsm->digiVerify(text1, sig1);
-            ASSERT(ok);
-
-            printf("digiSign() test 3\n");
-            ok = dsm->digiSign(text2, sig2.clear());
-            ASSERT(ok);
-            ok = dsm->digiVerify(text2, sig2);
-            ASSERT(ok);
-            ok = dsm->digiSign(text2, sig2.clear());
-            ASSERT(ok);
-            ok = dsm->digiVerify(text2, sig2);
-            ASSERT(ok);
-
-            printf("digiSign() test 4\n");
-            ok = dsm->digiVerify(text1, sig1);
-            ASSERT(ok);
-
-            printf("digiSign() test 5\n");
-            ok = dsm->digiVerify(text2, sig2);
-            ASSERT(ok);
-
-            printf("digiSign() test 6\n");
-            ok = dsm->digiVerify(text1, sig2);
-            ASSERT(!ok);//should fail
-
-            printf("digiSign() test 7\n");
-            ok = dsm->digiVerify(text2, sig1);
-            ASSERT(!ok);//should fail
-
-            printf("digiSign() test 8\n");
-            ok = dsm->digiSign(text3, sig3.clear());
-            ASSERT(ok);
-
-            printf("digiSign() test 9\n");
-            ok = dsm->digiVerify(text3, sig1);
-            ASSERT(!ok);//should fail
-            ok = dsm->digiVerify(text3, sig2);
-            ASSERT(!ok);//should fail
-            ok = dsm->digiVerify(text3, sig3);
-            ASSERT(ok);
-
-            //Perform
-            printf("digiSign() loop test\n");
-            unsigned now = msTick();
-            for (int x=0; x<1000; x++)
-            {
-                dsm->digiSign(text3, sig3.clear());
-            }
-            printf("digiSign() 1000 iterations took %d MS\n", msTick() - now);
-
-            printf("digiVerify() loop test\n");
-            now = msTick();
-            for (int x=0; x<1000; x++)
-            {
-                dsm->digiVerify(text3, sig3);
-            }
-            printf("digiverify 1000 iterations took %d MS\n", msTick() - now);
-
-            now = msTick();
-            printf("\nAsynchronous test digiSign\n");
-            asyncDigiSignUnitTest(dsm);
-            printf("digiSign 1000 async iterations took %d MS\n", msTick() - now);
-
-            now = msTick();
-            printf("\nAsynchronous test digiVerify\n");
-            asyncDigiVerifyUnitTest(dsm);
-            printf("digiverify 1000 async iterations took %d MS\n", msTick() - now);
-
-            now = msTick();
-            printf("\nAsynchronous test digiSign and digiVerify\n");
-            asyncDigiSignAndVerifyUnitTest(dsm);
-            printf("digiSign/digiverify 1000 async iterations took %d MS\n", msTick() - now);
-        }
-
-        catch (IException *e)
-        {
-            StringBuffer err;
-            e->errorMessage(err);
-            printf("Digisign IException thrown:%s\n", err.str());
-            exception.setown(e);
-        }
-        catch (CppUnit::Exception &e)
-        {
-            printf("Digisign CppUnit::Exception thrown\n");
-            cppunitException = e.clone();
-        }
-        printf("Completed executing digiSign() unit tests\n");
-    }
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION( DigiSignUnitTest );
-CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( DigiSignUnitTest, "DigiSignUnitTest" );
-
-#endif // _USE_CPPUNIT