Mind Chasers Inc.
Mind Chasers Inc.

Build, Debug, and Experiment with Master Branch of OpenSSL on Ubuntu Linux 18.04

A brief review of cloning, building, debugging, and experimenting with the master branch of the OpenSSL project. Includes stepping into the crypto library with GDB.

Overview

OpenSSL is the de facto, open source cryptography software package for Linux and other operating systems. It consists of both a command line tool and libraries for cryptography. It has cutting edge support of cryptographic algorithms and an experienced team of developers. However, there seems to be a dearth of recent documentation and examples regarding developing with OpenSSL, especially with the latest API. Therefore, this article is a brief summary on getting started with OpenSSL development.

The openssl executable itself is a command line tool that can be used interactively. A very simple example of using the openssl executable packaged with Ubuntu 18.04 is shown below:

# make sure we have the latest package installed on Ubuntu:
$ sudo apt upgrade openssl
Reading package lists... Done
Building dependency tree       
Reading state information... Done
openssl is already the newest version (1.1.0g-2ubuntu4.3).
...

$ which openssl
/usr/bin/openssl

$ openssl
OpenSSL> version
OpenSSL 1.1.0g  2 Nov 2017
OpenSSL> quit

Note that the following can be found on openssl.org: "The 1.1.0 series is currently only receiving security fixes and will go out of support on 11th September 2019."

Also, note that Ubuntu 18.10 (Cosmic) makes use of OpenSSL 1.1.1.

The version numbering system for OpenSSL can be confusing and is explained in Release Strategy. In this article, we work with the master branch, which tracks 3.0 development.

OpenSSL Components

OpenSSL relies on two important libraries that are part of the OpenSSL project:

  • libssl provides the client and server-side implementations for SSLv3 and TLS.
  • libcrypto provides general cryptographic and X.509 support needed by SSL/TLS but not logically part of it

We have found that the best way to learn how to work with the OpenSSL libraries is to look at how the openssl application makes use of them itself, and this is shown by example below.

Build Master Branch on Ubuntu 18.04

Below we show steps to clone, build, and use the master branch of the OpenSSL repository. We install it to /opt/openssl and make use of it in such a way that it does not collide with the version installed & supported by Ubuntu 18.04. You may wish to build and install your own version of OpenSSL in a virtual environment or container.

$ cd /build/  # /build is where we build our code

$ git clone https://github.com/openssl/openssl.git
$ cd /build/openssl

$ find . -name tls1.h | xargs grep TLS1_3_VERSION
# define TLS1_3_VERSION                  0x0304
# define TLS_MAX_VERSION                 TLS1_3_VERSION

$ mkdir build; cd /build/openssl/build

$ ../config -v --prefix=/opt/openssl --openssldir=$HOME/openssl --debug
../config -v --prefix=/opt/openssl --openssldir=$HOME/openssl --debug
Operating system: x86_64-whatever-linux2
/usr/bin/env __CNF_CPPDEFINES='' __CNF_CPPINCLUDES='' __CNF_CPPFLAGS='' __CNF_CFLAGS='' __CNF_CXXFLAGS='' __CNF_LDFLAGS='' __CNF_LDLIBS='' /usr/bin/perl ../Configure linux-x86_64 '--prefix=/opt/openssl' '--openssldir=/home/bcochran/openssl' '--debug'
Configuring OpenSSL version 3.0.0-dev for target linux-x86_64
Using os-specific seed configuration
Creating configdata.pm
Creating Makefile
...

config options used (see INSTALL for extensive list of supported options):

  • --prefix: top of the installation directory tree
  • --openssldir: directory for OpenSSL configuration files, and also the default certificate and key store.
  • --debug: build with debugging symbols

Note that OpenSSL doesn't use autotools for its build system but instead uses perl (scripts). The following is from the OpenSSL FAQ: "For OpenSSL 1.1, we decided to base our build system on perl, information files and build file (Makefile) templates, thereby covering all the systems we support. Perl was the base language of choice because we already use it in diverse scripts, and it's one of the most widely spread scripting languages."

Let's look a little further into how we configured the build. First, let's make sure we configured properly for debugging by looking at CFLAGS and CXXFLAGS

$ perl configdata.pm -m

Makevars:

    AR              = ar
    ARFLAGS         = r
    CC              = gcc
    CFLAGS          = -Wall -O0 -g
    CPPDEFINES      = 
    CPPFLAGS        = 
    CPPINCLUDES     = 
    CXX             = g++
    CXXFLAGS        = -Wall -O0 -g
    HASHBANGPERL    = /usr/bin/env perl
    LDFLAGS         = 
    LDLIBS          = 
    PERL            = /usr/bin/perl
    RANLIB          = ranlib
    RC              = windres
...

Let's also look at what's enabled and disabled:

$ perl configdata.pm -o

Enabled features:

    afalgeng
    aria
    asm
    async
    autoalginit
    autoerrinit
    autoload-config
    bf
    blake2
    camellia
    capieng
    cast
    chacha
    cmac
    cms
    comp
    crmf
    ct
    deprecated
    des
    dgram
    dh
    dsa
    dtls
    dynamic-engine
    ec
    ec2m
    ecdh
    ecdsa
    engine
    err
    filenames
    fips
    gost
    idea
    legacy
    makedepend
    md4
    mdc2
    module
    multiblock
    nextprotoneg
    pinshared
    ocb
    ocsp
    padlockeng
    pic
    poly1305
    posix-io
    psk
    rc2
    rc4
    ...
    tls1
    tls1-method
    tls1_1
    tls1_1-method
    tls1_2
    tls1_2-method
    tls1_3
    dtls1
    dtls1-method
    dtls1_2
    dtls1_2-method

Disabled features:

    ktls                    [default] OPENSSL_NO_KTLS
    asan                    [default] OPENSSL_NO_ASAN
    buildtest-c++           [default] 
    crypto-mdebug           [default] OPENSSL_NO_CRYPTO_MDEBUG
    crypto-mdebug-backtrace [default] OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
    devcryptoeng            [default] OPENSSL_NO_DEVCRYPTOENG
    ec_nistp_64_gcc_128     [default] OPENSSL_NO_EC_NISTP_64_GCC_128
    egd                     [default] OPENSSL_NO_EGD
    external-tests          [default] OPENSSL_NO_EXTERNAL_TESTS
    fuzz-libfuzzer          [default] OPENSSL_NO_FUZZ_LIBFUZZER
    fuzz-afl                [default] OPENSSL_NO_FUZZ_AFL
    md2                     [default] OPENSSL_NO_MD2 (skip crypto/md2)
    msan                    [default] OPENSSL_NO_MSAN
    rc5                     [default] OPENSSL_NO_RC5 (skip crypto/rc5)
    sctp                    [default] OPENSSL_NO_SCTP
    ssl-trace               [default] OPENSSL_NO_SSL_TRACE
    trace                   [default] OPENSSL_NO_TRACE
    ubsan                   [default] OPENSSL_NO_UBSAN
    unit-test               [default] OPENSSL_NO_UNIT_TEST
    weak-ssl-ciphers        [default] OPENSSL_NO_WEAK_SSL_CIPHERS
    zlib                    [default] 
    zlib-dynamic            [default] 
    ssl3                    [default] OPENSSL_NO_SSL3
    ssl3-method             [default] OPENSSL_NO_SSL3_METHOD
$ make

$ make install

$ export PATH=/opt/openssl/bin:$PATH
$ export LD_LIBRARY_PATH=/build/openssl/build

$ which openssl
/opt/openssl/bin/openssl

$ openssl
OpenSSL> version
OpenSSL 3.0.0-dev xx XXX xxxx
OpenSSL> quit

Note that the two exports above are temporary. It is probably most conveinent to add these two lines to a file (e.g., openssl-env) that you would then source:

$ which openssl
/usr/bin/openssl

$ source openssl-env

$ which openssl
/opt/openssl/bin/openssl

$ ldd /opt/openssl/bin/openssl 
	linux-vdso.so.1 (0x00007fff0abe6000)
	libssl.so.3 => /opt/openssl/lib/libssl.so.3 (0x00007f74fd5c3000)
	libcrypto.so.3 => /opt/openssl/lib/libcrypto.so.3 (0x00007f74fd056000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f74fce37000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f74fca46000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f74fc842000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f74fdb4e000)

Now your set up to experiment with the master branch of OpenSSL in your current shell whenever your wish on your Ubuntu 18.04 system.

Debugging genrsa

Generating an RSA private key is a common and interesting function for the openssl application. Below we'll set up a breakpoint in an RSA crypto library function, which will be called when we invoke genrasa:

$ gdb -v
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git

$ gdb openssl
...
Reading symbols from openssl...done.
(gdb) break main
Breakpoint 1 at 0x4e8d8: file ../apps/openssl.c, line 248.

(gdb) run
Starting program: /opt/openssl/bin/openssl 
...
Breakpoint 1, main (argc=1, argv=0x7fffffffe3d8) at ../apps/openssl.c:248
248	{

(gdb) break rsa_builtin_keygen
Breakpoint 2 at 0x7ffff77cdaa4: file ../crypto/rsa/rsa_gen.c, line 67.

(gdb) cont
Continuing.
OpenSSL> genrsa
Generating RSA private key, 2048 bit long modulus (2 primes)

Breakpoint 2, rsa_builtin_keygen (rsa=0x55555584d5a0, bits=2048, primes=2, e_value=0x55555584d580, cb=0x55555584d560)
    at ../crypto/rsa/rsa_gen.c:67
67	{

(gdb)  backtrace
#0  rsa_builtin_keygen (rsa=0x55555584d5a0, bits=2048, primes=2, e_value=0x55555584d580, cb=0x55555584d560)
    at ../crypto/rsa/rsa_gen.c:67
#1  0x00007ffff77cda76 in RSA_generate_multi_prime_key (rsa=0x55555584d5a0, bits=2048, primes=2, e_value=0x55555584d580, 
    cb=0x55555584d560) at ../crypto/rsa/rsa_gen.c:62
#2  0x000055555559d650 in genrsa_main (argc=0, argv=0x55555584d4b8) at ../apps/genrsa.c:153
#3  0x00005555555a39e2 in do_cmd (prog=0x555555848070, argc=1, argv=0x55555584d4b0) at ../apps/openssl.c:781
#4  0x00005555555a2ccb in main (argc=1, argv=0x7fffffffe3d8) at ../apps/openssl.c:380

(gdb) list
62	    return rsa_builtin_keygen(rsa, bits, primes, e_value, cb);
63	}
64	
65	static int rsa_builtin_keygen(RSA *rsa, int bits, int primes, BIGNUM *e_value,
66	                              BN_GENCB *cb)
67	{
68	#ifdef FIPS_MODE
69	    if (primes != 2)
70	        return 0;
71	    return rsa_sp800_56b_generate_key(rsa, bits, e_value, cb);

Now at this point, you can step through the RSA key generation routine, print variables, dump memory, etc. If you need help with GDB, then consult its documentation page.

Troubleshooting

Keep in mind that OpenSSL includes a test suite:

$ make tests
make depend && make _tests
make[1]: Entering directory '/build/openssl/build'
make[1]: Leaving directory '/build/openssl/build'
make[1]: Entering directory '/build/openssl/build'
( cd test; \
  mkdir -p test-runs; \
  SRCTOP=../.. \
  BLDTOP=../. \
  RESULT_D=test-runs \
  PERL="/usr/bin/perl" \
  EXE_EXT= \
  OPENSSL_ENGINES=`cd .././engines 2>/dev/null && pwd` \
  OPENSSL_DEBUG_MEMORY=on \
    /usr/bin/perl ../../test/run_tests.pl  )
../../test/recipes/01-test_abort.t .................... ok   
../../test/recipes/01-test_sanity.t ................... ok   
../../test/recipes/01-test_symbol_presence.t .......... ok   
../../test/recipes/01-test_test.t ..................... ok   
../../test/recipes/02-test_errstr.t ................... ok     
...
../../test/recipes/99-test_ecstress.t ................. ok   
../../test/recipes/99-test_fuzz.t ..................... ok     
All tests successful.
Files=168, Tests=1493, 132 wallclock secs ( 1.14 usr  0.16 sys + 129.64 cusr  9.09 csys = 140.03 CPU)
Result: PASS
make[1]: Leaving directory '/build/openssl/build'

Also, the OpenSSL FAQ page includes a lot of troubleshooting information under "Questions on Building and Testing OpenSSL"

Simple TLS Server

The OpenSSL wiki provides a "complete implementation of a minimal TLS server". Let's copy it to tlsserver.c in our home directory and try it.

We'll make two small changes to tlsserver.c to make it easier to understand what's happening:

-    const char reply[] = "test\n";
+    const char reply[] = "Hello from Simple TLS Server\n";
        else {
+           printf("served\n");
            SSL_write(ssl, reply, strlen(reply));
        }

Note that the below example is kept as simple as possible and is just for testing and experimenation. Do not use to pass private information.

$ source openssl-env

$ gcc -O0 -g tlsserver.c -o tlsserver -L/opt/openssl/lib -lcrypto -lssl

$ ldd tlsserver
	linux-vdso.so.1 (0x00007ffe80e8d000)
	libcrypto.so.3 => /opt/openssl/lib/libcrypto.so.3 (0x00007f20e157c000)
	libssl.so.3 => /opt/openssl/lib/libssl.so.3 (0x00007f20e12c7000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f20e0ed6000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f20e0cd2000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f20e0ab3000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f20e1cec000)


 ./tlsserver 
140681987502528:error:02001002:system library:fopen:No such file or directory:../crypto/bio/bss_file.c:290:fopen('cert.pem','r')
140681987502528:error:20074002:BIO routines:file_ctrl:system lib:../crypto/bio/bss_file.c:292:
140681987502528:error:140AD002:SSL routines:SSL_CTX_use_certificate_file:system lib:../ssl/ssl_rsa.c:390:

It probably comes as no surprise that we need to set up a private key and certificate:

$ openssl genrsa -out key.pem 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.
.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.
e is 65537 (0x010001)

# for this example, hit return for all questions except Common Name
$ openssl req -new -x509 -key key.pem -out cert.pem -days 7
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []: <host name>
Email Address []:

Try it again:

$ ./tlsserver

And from a shell on a different machine:

$ curl --insecure --tlsv1.3 https://<host name>:4433
curl: (4) OpenSSL was built without TLS 1.3 support
# bummer, let's try it again and realize we have some work to do on this other machine

$ curl --insecure https://<host name>:4433
Hello from Simple TLS Server

Note that we pass curl the --insecure option since we are using a self signed certificate.

Another fun thing to do at this point is to run either Wireshark or tcpdump and watch the exchange of the TLS handshake. We'll try to update this article soon to show Wireshark displaying a TLS1.3 handshake with this simple server & cURL example.

References

Terms / Acronyms

  • CMVP: Cryptographic Module Validation Program
  • FIPS: Federal Information Processing Standards
  • PEM: Privacy-enhanced Electronic Mail is the origin of this acronym, but it doesn't convey that it is a file format for OpenSSL
  • RSA: Popular Asymmetric / Private Key Crypto Algorithm, named after its inventors: Rivest–Shamir–Adleman
  • SSL: Secure Socket Layer
  • TLS: Transport Layer Security

Didn't find an answer to your question? Post your issue below or in our new FORUM, and we'll try our best to help you find a solution.

And please note that we update our site daily with new content related to our open source approach to network security and system design. If you would like to be notified about these changes, then please follow us on Twitter and join our mailing list.

Related articles on this site:

share
subscribe to mailing list:

Please help us improve this article by adding your comment or question:

your email address will be kept private
authenticate with a 3rd party for enhanced features, such as image upload
previous month
next month
Su
Mo
Tu
Wd
Th
Fr
Sa
loading