I originally published the following article with the InfoSec Institute, but I figured I would re-publish it on my personal blog.
Perhaps the most devastating vulnerability in recent years was OpenSSL’s Heartbleed exposure. This is just the latest in a series of major vulnerabilities affecting a linchpin security software package. Why does this continue to happen? What are the solutions? Moreover, could LibreSSL be an industry-level replacement to OpenSSL?
OpenSSL was initially developed in 1995 as a commercial-grade, open-source and fully featured toolkit for implementing Secure Socket Layer (SSL) and Transport Layer Security (TLS), and as a general-purpose cryptographic software library. OpenSSL was adopted by various operating systems and major applications as their primary cryptographic library, including all main Linux distributions, BSD variants, Solaris and is heavily utilized by Windows. However, perhaps due to its success, rapid adaption and diverse implementation, the quality of the code eroded. What should be mature code with a relatively simple and concise objective has grown to become an array of tangentially related features, patchwork of “quick fixes” and excessive backwards compatibility and portability to the detriment of the security of product.
Enter LibreSSL. LibreSSL began as a fork of OpenSSL 1.0.1g. Developed by the OpenBSD team, LibreSSL is designed to be a drop-in replacement of OpenSSL. Its stated goals are code modernization, security and software development best practice. In the past 18 months, the code has made impressive strides in said goals. It has also made controversial decisions, including removing widely used features and jettisoning oft-used government standards. Could this software package replace OpenSSL?
This paper seeks to compare OpenSSL and LibreSSL as the main encryption library for production environments by:
- Direction and Progress
- Compatibility and Portability
Code Modernization and Cleanup
A primary critique of OpenSSL is that the code-sacrificed industry best practices, code review and remediation, and structured development in favor of rapid portability and functionality. Specifically, they argued the following problems:
- Multiple implementations of a task, depending on the OS, architecture or compiler, resulting in multiple points of failure.
- Unorthodox implementations to accommodate failures in Operating Systems, architectures or compilers.
- Code unreadability, such as a labyrinth of C preprocessor statements.
- Custom implementation of standard C library calls, such as printf() or memory management.
- A patchwork of “quick-fixes” rather than solving fundamental design flaws.
OpenSSL aims for portability across all systems, including those that do not support standard C functions. It accomplishes this by creating an abstraction layer and reimplementing APIs that should be in provided by the OS. This resulted in a non-standard OpenSSL C Library variant, distinct from standard C that does not undergo the same level of public scrutiny, maturity and secure best practice.
LibreSSL tackles these issues assuming that the code is designed for a modern, POSIX compliant OS on a modern architecture using a compiler that conforms to standard C. Namely, LibreSSL assumes that the target system is OpenBSD. In cases where an OpenBSD-specific C call does not exist, LibreSSL ports only that function and maintains the same symbol, as not to create an abstraction layer.
Additionally, this assumption afforded LibreSSL developers the freedom to remove antiquated, CPU-specific, OS-specific, compiler-specific or otherwise non-standard code. One example of this is the get_ip() function which parses an IP address string and returns each octet in a character array. Rather than utilizing gethostbyname(), which became part of the GNU C Library 1997, OpenSSL chose this implementation because Visual C 1.52c, dated 1995, contained an unresolved DLL error with the sscanf() function. To date, LibreSSL removed 140,000 lines of code, or 23%.
OpenSSL insisted on portability on every platform, irrespective of its capabilities. This became problematic when the OS or the C Library was unable to meet cryptographic requirements. Examples included:
- Operating systems that did not contain the malloc(3) function
- Unacceptable latency when allocating or freeing memory
- Inability to allocate 64-bit objects
OpenSSL solved these problems by developing an internal memory management system, distinct from the OS’s. This layer was a fixed Last-in-First-out stack that assigned memory to requesting objects, but never sanitized or freed used memory back to the OS. While this assisted in portability, it created numerous challenges, namely:
- OpenSSL code maintenance and readability became extremely difficult.
- Common debuggers, such as Valgrind, are designed to analyze fixed-buffers assigned to individual variables. Abstracting memory management from the OS made buffer overflow detection virtually impossible.
- The LIFO memory architecture virtually ensured that exposed memory contains sensitive information, as evident by the Heartbleed vulnerability.
- Creating memory leaks that could not be detected by garbage collectors.
LibreSSL simplified the process by shifting the responsibility of memory management back to the OS. To this end, LibreSSL entirely removed the OpenSSL memory management layer, simplified the higher-level macros, and utilized POSIX C libraries. Developers comment that this revealed thousands of buffer overflow errors, which were subsequently corrected.
The OpenSSL Software Foundation (OSF) appeared slow or uninterested in remediating documented issues. As a metric, upon LibreSSL’s initial release in April 2014, OpenSSL’s bug ticket system contained 1104 open tickets with the earliest dating back to April 2002. To date, 115 item from that list still unremediated. The stagnation of ticket remediation exacerbated disinterest in community contribution. As part of its goal, LibreSSL remediated all open tickets and shared newly vulnerability newly identified vulnerabilities to the OpenSSL team.
Security is a central goal for LibreSSL, which they aim to achieve by increasing code readability and review, removing insecure functionality and memory sanitization.
As discussed, LibreSSL assumes a level of competence of the underlying platform. This afforded LibreSSL developers the freedom to remove large swaths of confusing or unwieldy code, such as nested preprocessor statements for specific OSs or architectures. Though this may not immediately result in enhanced security, it has allow outside developers to review the code with greater ease and contribute to security and overall quality.
With the disclosure of the POODLE vulnerability, security analysts deemed SSLv3 insecure and not for use in production environments. However, many environments still require SSLv3 for legacy purposes and are unable to migrate to TLSv1.1. To this end, OpenSSL maintains backwards compatibility with SSLv3 and all applications compiled for SSLv3 are still operational. Conversely, LibreSSL 2.2.2 entirely removed SSLv3 support. Additionally, LibreSSL removed weak or broken cipher suites, including the Secure Remote Password (SRP), Pre-Shared Key (PSK) and all Export ciphers. LibreSSL added support for ChaCha, GOST and IDEA ciphers. While LibreSSL’s decisions provide technically enhanced security, this may be negatively impactful in environments that require legacy SSLv3 support.
Random number generation is a critical aspect of cryptography. Without proper seeding, an attacker is able to predict private encryption keys.
OpenSSL relies on the operating system to generate random numbers. On UNIX and Unix-like systems, this typically means reading from /dev/urandom or /dev/random. However, in the event that OS is unable to generate random numbers, OpenSSL provides the RAND_add(3SSL) and RAND_seed(3SSL) API calls, allowing users to seed the PRNG function.
LibreSSL shifts the responsibility of random number generation entirely onto the OS. LibreSSL vestigially maintains the function for binary compatibility, but removed all associated code. It is worth noting that due to the lack of seeding, the initial release of LibreSSL contained a critical flaw in the PRNG function, where two forked processes contained the same PRNG seed and thus generated the same random data. This vulnerability was quickly patched.
Memory sanitization is a central feature in LibreSSL that is lacking in OpenSSL. Prior to the deallocation of objects, LibreSSL explicitly zeros out memory using OpenBSD’s explicit_bzero(3) function. This proactively reduces the impact of memory exposure in the event of a future vulnerability or an unprivileged process that gains control of a tainted memory segment. The LibreSSL team created portable a module for OSs that do not have these OpenBSD-specific API calls.
Common Vulnerability Enumerations
Since LibreSSL is a fork of OpenSSL 1.0.1g, it is subject to many of the same issues and vulnerabilities that affect many of OpenSSL. From the date of LibreSSL’s initial release in April 2014, to today, there are 45 CVEs that affect OpenSSL. Of those, only twenty-four affected LibreSSL, with another one vulnerability only affects LibreSSL. This is demonstrable proof that LibreSSL has made strides in security.
FIPS 140-2 Compliance
The OSF sought to make OpenSSL FIPS 140-2 compliance. Consequently, OSF created the OpenSSL FIPS Object Module, which provides an API to invoke FIPS approved cryptographic functions. This is particularly important for Federal IT systems that are obligated to comply with FIPS 140-2. Conversely, LibreSSL removed the FIPS compliant module from its code, arguing that the FIPS 140-2 uses weak or broken ciphers and is detrimental to security. LibreSSL is neither FIPS 140-2 compliant nor is it a goal of the project. This is potentially impactful for risk-averse government IT systems that are required to comply with FIPS 140-2.
As discussed, LibreSSL was written specifically for OpenBSD and the OpenBSD team ported LibreSSL for modern OSs that conform to POSIX standards. Consequently, legacy systems or systems that do not conform to POSIX are unable to run LibreSSL.
LibreSSL aims to be a drop-in replacement to OpenSSL with no changes to existing applications. LibreSSL achieves compatibility by maintaining exposed API calls, even if their functionality is nullified. However, many applications, such as Apache Web Server, must be minorly patched before it will link with LibreSSL. This is mostly due to removed features or removing unnecessarily exposed interfaces.
The LibreSSL team ported it to numerous operating systems, including Linux, Windows, FreeBSD, OS X, and Solaris. It is worth noting that outside of OpenBSD, no operating system has pre-compiled package-level support for LibreSSL. Thus, all binaries and applications must be manually compiled and manually upgraded. LibreSSL explicitly removed support for Classic Mac OS, 16-bit Windows, DOS, VAS, and OS/2.
Performance tests were performed on a stock Ubuntu 14.04 LTS install running kernel 3.19.0-28-generic using Apache 2.4.16. Both virtual machines were running on a single core from an Intel(R) Core(TM) i7-4712MQ CPU @ 2.30GHz CPU with 2 gigabytes of RAM. The tests were running OpenSSL 1.0.1g. and LibreSSL 2.3.0, respectively. The test utilized ApacheBench version 2.3 with 100,000 requests per test for a payload of 45-bytes.
In terms of speed, OpenSSL outperforms or is at best comparable to LibreSSL across all tested ciphers. The performance impact is attributed to the explicit memory sanitization operations within LibreSSL that are not present in OpenSSL and optimization features that were disabled in LibreSSL.
Both LibreSSL and OpenSSL have advantages and disadvantages, depending on a given application, requirement or tolerance.
From a purely security perspective, LibreSSL is the clear winner, by proactively addressing security concerns, disabling broken ciphers and protocols, and building security into the design. OpenSSL employs a reactive approach and fails to address adequately security in design. In addition, while they still share many of the same vulnerabilities, one can anticipate that LibreSSL will be subject to a diminishing number of shared vulnerabilities and less overall going forward.
However, this approach also has a downside. LibreSSL’s focus on security means that it is not backwards compatible with deprecated ciphers or protocols and therefore will not function in legacy environments. Additionally, LibreSSL’s stated refusal to comply with FIPS 140-2 effectively guarantees government systems and enterprise-level OSs will never utilize it.
OpenSSL outperforms LibreSSL in portability. This is not due to a failure in LibreSSL, but is instead a testament to OpenSSL’s successful adaptation. Since no major operating system currently supports LibreSSL, LibreSSL and dependent applications must be manually compiled and installed, rather than utilizing the package management systems in FreeBSD, Debian, Ubuntu or Redhat.
In terms of performance, OpenSSL exceeds LibreSSL across all ciphers. As stated, this is attributable to LibreSSL’s explicit memory sanitization operations. The performance may be negligible in low-availability applications, but can be impactful in large, latency-sensitive production environments.
Both LibreSSL and OpenSSL have their strengths and weaknesses. OpenBSD has demonstrated that LibreSSL is ready for production environments, but has yet to be widely deployed. Ultimately, the choice between LibreSSL and OpenSSL is a variant of the perennial question of security versus functionality.