0

I found out that OPENSSL_cleanse wastes a lot of time in my project. For example, if it runs for 25 seconds, 3 seconds are wasted by OPENSSL_cleanse. I checked the code of this function and decided that it isn't doing anything very useful for me. I know it fills memory with garbage data for security reasons but I don't really care about it. So I decided to place return; just before the start of any operations in this function.

void OPENSSL_cleanse(void *ptr, size_t len)
{
    return;
    // original OpenSSL code goes here
}

I'm using Mac OS and Xcode. I've compiled the lib and installed it in /Users/ForceBru/Desktop/openssl via the --openssldir option of the Configure script. I've added it to my project in Build Settings->Link Binary With Libraries and added include dirs in Build Settings->Search Paths->Header Search Paths and Build Settings->Search Paths->Library Search Paths.

The project compiled fine, but the time profiler still shows pretty expensive calls to OPENSSL_cleanse.

Edit: the C tag is because OpenSSL is written in C, and the C++ tag is because my code is in C++. Maybe this information will be helpful.

The question is, what am I doing wrong? How do I remove the calls to OPENSSL_cleanse? I think this has to do with linking, because the command line includes -lcrypto, which means this library can actually be taken from anywhere (right?), not necessarily from /Users/ForceBru/Desktop/openssl.

Edit #2: I've edited the linker options to use the .a file in /Users/ForceBru/Desktop/openssl and removed it from Build Settings->Link Binary With Libraries. Still no effect.

Community
  • 1
  • 1
ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • I think you are correct, this is probably the reason. You might want to link your modified instance of openssl statically to your project (I do _not_ suggest to link a completely static binary, just the openssl library) – Ctx Feb 28 '16 at 16:23
  • @Ctx, how do I do it? – ForceBru Feb 28 '16 at 16:24
  • Locate the correct versions of your custom openssl library libssl.a and libcrypto.a and specify these on your linker command line with full path. Drop `-lssl` and `-lcrypto` from it in turn. – Ctx Feb 28 '16 at 16:28
  • @Ctx, I've done it, but it `OPENSSL_cleanse` is still there – ForceBru Feb 28 '16 at 16:37
  • You could check if the libcrypto.a you use indeed is your modified version with `objdump -d libcrypto.a` and see if it indeed returns without doing anything else. Otherwise something went already wrong with customizing openssl. – Ctx Feb 28 '16 at 16:41
  • @Ctx, checked that and it shows that `OPENSSL_cleanse` isn't just `return`ing, but doing what it was supposed to do. I tried removing it completely and replacing its declaration with `#define OPENSSL_cleanse(a, b)` in `crypto/crypto.h`, but now when I try to link with the library, I'm getting an error: `Undefined symbols for architecture x86_64: _OPENSSL_ia32cap_P referenced from _sha1_block_data_order in libcrypto.a`. `OPENSSL_cleanse` is still there even after removing it from the code completely, by the way. – ForceBru Feb 28 '16 at 16:59

2 Answers2

0

It turns out that OpenSSL has lots of assembly code generated by some Perl scripts that are located in the crypto directory (*cpuid.pl). These scripts generate assembly code for the following architectures: alpha, armv4, ia64, ppc, s390x, sparc, x86 and x86_64.

When make runs, the appropriate script fires generating a *cpuid.S (where * is one of the architectures mentioned earlier). These files are compiled into the library and seem to override the OPENSSL_cleanse implemented in crypto/mem_clr.c.

What I had to do is to simply change the body of OPENSSL_cleanse to ret in x86_64cpuid.pl:

.globl OPENSSL_cleanse
.type OPENSSL_cleanse,\@abi-omnipotent
.align 16
OPENSSL_cleanse:
    ret
    # loads of OPENSSL assembly
.size OPENSSL_cleanse,.-OPENSSL_cleanse
ForceBru
  • 43,482
  • 10
  • 63
  • 98
0

This isn't quite the answer that you were looking for, but it may help you along...


Removing OPENSSL_cleanse from OpenSSL-1.0.1r...
I checked the code of this function and decided that it isn't doing anything very useful for me...

That's probably a bad idea, but we would need to know more about your threat model. Zeroization allows you to deterministically remove sensitive material from memory.

Its also a Certification and Accreditation (C&A) item. For example, FIPS 140-2 requires zeroization even at Level 1.

Also, you can't remove OPENSSL_cleanse per se because OPENSSL_clear_realloc, OPENSSL_clear_free and friends call it. Also see the OPENSSL_cleanse man page.


For example, if it runs for 25 seconds, 3 seconds are wasted by OPENSSL_cleanse

OK, so this is a different problem. OPENSSL_cleanse is kind of convoluted, and it does waste some cycles in an effort to survive the optimization pass.

If you check Commit 380f18ed5f140e0a, then you will see it has been changed in OpenSSL 1.1.0 to the following. Maybe you could use it instead?

diff --git a/crypto/mem_clr.c b/crypto/mem_clr.c
index e6450a1..3389919 100644 (file)
--- a/crypto/mem_clr.c
+++ b/crypto/mem_clr.c
@@ -59,23 +59,16 @@
 #include <string.h>
 #include <openssl/crypto.h>

-extern unsigned char cleanse_ctr;
-unsigned char cleanse_ctr = 0;
+/*
+ * Pointer to memset is volatile so that compiler must de-reference
+ * the pointer and can't assume that it points to any function in
+ * particular (such as memset, which it then might further "optimize")
+ */
+typedef void *(*memset_t)(void *,int,size_t);
+
+static volatile memset_t memset_func = memset;

 void OPENSSL_cleanse(void *ptr, size_t len)
 {
-    unsigned char *p = ptr;
-    size_t loop = len, ctr = cleanse_ctr;
-
-    if (ptr == NULL)
-        return;
-
-    while (loop--) {
-        *(p++) = (unsigned char)ctr;
-        ctr += (17 + ((size_t)p & 0xF));
-    }
-    p = memchr(ptr, (unsigned char)ctr, len);
-    if (p)
-        ctr += (63 + (size_t)p);
-    cleanse_ctr = (unsigned char)ctr;
+    memset_func(ptr, 0, len);
 }

Also see Issue 455: Reimplement non-asm OPENSSL_cleanse() on OpenSSL's GitHub.


How do I remove the calls to OPENSSL_cleanse?

OK, so this is a different problem. You have to locate all callers and do something with each. It looks like there's about 185 places you will need to modify things:

$ cd openssl
$ grep -IR _cleanse * | wc -l
     185

Instead of this:

void OPENSSL_cleanse(void *ptr, size_t len)
{
    return;
    // original OpenSSL code goes here
}

Maybe you can delete the function, and then:

#define OPENSSL_cleanse(x, y)

Then the function calls becomes a macro that simply disappears during optimization. Be sure to perform a make clean after changing from a function to a macro.

But I would not advise doing so.


The project compiled fine, but the time profiler still shows pretty expensive calls to OPENSSL_cleanse.

My guess here is either (1) you did not perform a make clean after the changes to the OpenSSL library, or (2) you compiled and linked to the wrong version of the OpenSSL library. But I could be wrong on both.

You can see what your executable's runtime dependencies are with otool -L. Make sure its the expected one. Also keep in mind OpenSSL does not use -install_name.

Before you run your executable, you can set DYLD_LIBRARY_PATH to ensure the dylib you are modifying is loaded. Also see the dyld(1) man pages.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • I provided a link to my project so you can see that I really don't care about that security reason. It's just a brute-force program, and what I need is just speed, so any time consumers must be removed. No, changing `OPENSSL_cleanse` to a macro gives linker errors due to that assembly code I was talking about in my answer. What I don't understand now, is the purpose of the function in `mem_clr.c`. Why it is here when there's assembly for it? – ForceBru Feb 29 '16 at 12:45
  • @ForceBru - *"What I don't understand ... the purpose of the function in mem_clr.c. Why it is here when there's assembly for it..."* - I'm not sure about that. I think I share your understanding of things, so its kind of a mystery to me, too. – jww Feb 29 '16 at 13:50