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.
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
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.