How to develop Linux applications for FIPS on Ubuntu
Nikos Mavrogiannopoulos
on 6 September 2021
Tags: 20.04 , developers , Development , FIPS , Security , Security certifications and compliance
This is the second article in our series regarding FIPS 140 and Ubuntu. The first part of this series, this article, covers running FIPS 140 applications on Ubuntu while this part is focused on the development of FIPS 140 applications on Ubuntu.
What is FIPS and why should a developer care?
Developing applications for regulated and high security environments can be challenging. There is a plethora of software following diverse development methods and standards, but not always targeting a particular data protection standard. How can a large organization be assured that the cryptographic applications and libraries used implement cryptography correctly and follow best practices? FIPS 140 tackles the cryptography validation problem from the perspective of the U.S. regulator. To learn more about FIPS check the first article on this topic. In essence the FIPS 140 standard ensures that cryptography is implemented using well known secure designs, follows certain best practices, does not involve obscure algorithms, and that there is a due process in attestation.
Why treat cryptography differently?
There are very good reasons to rely on well known secure designs; the main being that cryptography is hard and easy to get wrong. Small mistakes–such as applying the wrong encryption mode–may render the data encrypted by an application recognizable even when encrypted. The following picture (inspired by the image in this great wikipedia article) is a good example of what can happen when one applies an encryption mode that is not suitable for the task.
Original image | Encrypted image using AES in ECB mode | Encrypted image using AES in CTR mode |
Clearly the AES cipher in electronic codebook (ECB) mode is not good enough to hide patterns in images. The counter (CTR) mode used in the rightmost image seems to be a more suitable encryption mode, right? After all, CTR mode is an integral part of GCM used by TLS today. Let’s see what happens when more images are encrypted using the CTR mode and the same key.
Encrypted image using AES in CTR mode | Different image encrypted using AES in CTR mode but using the same key as the previous one | The XOR of the previous two encrypted images. Can you guess the original images? |
The encrypted images were revealed. So even encryption modes that are widely used for one purpose, like secure communications, cannot be naively applied for other purposes such as encryption for storage.
Nonetheless, even if the most appropriate algorithm and mode are used, the security landscape changes so fast that secure software of 10 years ago can no longer withstand attacks that exploit newly discovered vulnerabilities. For instance, timing issues in cryptographic implementation, or timing issues in CPU instructions like Spectre and Meltdown, weren’t as widely understood as they are today. Actions like bundling cryptographic code in an application and not updating it for years “because it works” result in applications that eventually become insecure.
Knowing these, we can see the reasons behind the FIPS 140 standard treating cryptography differently.
How can I make my application comply with FIPS?
There are two ways one can make an application or solution comply with FIPS 140. One is to design it with the FIPS 140 requirements in mind, and go through the cryptographic validation/certification process for the whole application. The other is to consume the FIPS-validated cryptographic components from the operating system and ensure that the application follows the appropriate guidance and doesn’t introduce new cryptography. This post is about the latter: how you can use Ubuntu to comply with FIPS 140 requirements.
What are the FIPS validated cryptographic packages?
The available FIPS validated cryptographic packages in Ubuntu 20.04 are the following:
Package name | Description |
---|---|
linux-image-fips | The Linux Kernel Crypto API. |
libssl1.1 | The OpenSSL cryptographic backend. This includes the necessary cryptography for OpenSSH as well. |
libgcrypt20 | The libgcrypt cryptographic library. |
strongswan | StrongSwan, the IPSec VPN implementation. |
Can I develop FIPS-compliant applications with Python, Perl, Ruby, and Nodejs?
The short answer is yes, you can develop applications with Python, Ruby, Perl and Nodejs on Ubuntu using the FIPS validated OpenSSL package. That is because these languages either use OpenSSL directly for their cryptographic operations or provide hooks for it. In Python for example, you can use cryptography from python3-cryptography.
How do I use the FIPS validated components correctly?
In addition to its development documentation, each validated package comes with a security policy attached to its certificate which provides detailed guidance about using the module in the “User Guidance” section. You can find the security policy document after clicking on the certificate number on the table above, under the section ‘Related Files’. You can find all certificates and policies issued by Canonical by querying the NIST website.
For the libraries like OpenSSL and libgcrypt, these instructions contain guidance about particular algorithms, for example, where to apply the AES-XTS algorithm and details about initialization and other aspects relevant to the package.
How can I develop with the FIPS packages?
To start development, you need to enable FIPS on an LTS Ubuntu release, such as 18.04 or 20.04 with a subscription. As Ubuntu’s mission is to bring free software to the widest audience, independent developers and individuals can access the FIPS packages through a free personal subscription. For developing and running workloads with FIPS on the enterprise, the validated packages are available with Ubuntu Pro or an Ubuntu Advantage subscription.
The following instructions will enable FIPS mode on Ubuntu LTS and install the openssl development library.
Step 1: attach your subscription
Obtain your subscription token from myasnchisdf.eu.org/advantage and attach it to your system. This step is not necessary on Ubuntu Pro.
$ sudo apt update
$ sudo apt install ubuntu-advantage-tools
$ sudo ua attach <TOKEN>
Step 2: enable FIPS and install the development files
This step enables the FIPS mode and installs the OpenSSL development files.
$ sudo ua enable fips-updates
The previous command hides a lot of complexity relating to FIPS mode. It installs the packages from the FIPS repository, and adds a kernel command line option to enable FIPS. A reboot is necessary for the system to boot in FIPS mode.
To install the OpenSSL development files run the following commands.
$ sudo apt update
$ sudo apt install -y libssl-dev
Step 3: compile and run an application
Developing applications on FIPS-enabled systems is not much different than on normal systems. Let’s try to show the differences using the sample application below and openssl. The application takes a fixed size message “abc” and prints its hash.
#include <openssl/evp.h>
#include <openssl/err.h>
static
int digest_message(const unsigned char *message, size_t message_len,
unsigned char *output, unsigned int max_output_size,
const EVP_MD *type)
{
EVP_MD_CTX *mdctx;
int output_size = EVP_MD_size(type);
if (output_size > max_output_size)
return -1;
if ((mdctx = EVP_MD_CTX_new()) == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
if (1 != EVP_DigestInit_ex(mdctx, type, NULL)) {
ERR_print_errors_fp(stderr);
return -1;
}
if (1 != EVP_DigestUpdate(mdctx, message, message_len)) {
ERR_print_errors_fp(stderr);
return -1;
}
if (1 != EVP_DigestFinal_ex(mdctx, output, &output_size)) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_MD_CTX_free(mdctx);
return output_size;
}
int main()
{
unsigned char out[64];
int out_len;
int i;
out_len = digest_message("abc", 3, out, sizeof(out), EVP_md5());
if (out_len == -1) {
fprintf(stderr, "could not hash\n");
return 1;
}
for (i = 0; i < out_len; i++) {
printf("%.2x", out[i]);
}
fputc('\n', stdout);
return 0;
}
Let’s now try to compile and run it on the Ubuntu system that we previously enabled FIPS on.
$ gcc app.c -lcrypto -o app && ./app
140652539485056:error:060800C8:digital envelope routines:EVP_DigestInit_ex:disabled for FIPS:../crypto/evp/digest.c:135:
could not hash
The application failed to run and no hash was produced. The reason for that is that the application is using the legacy MD5 hash algorithm which is not allowed in FIPS 140-2 enabled systems. That is, the Ubuntu FIPS mode prevents applications from using unapproved algorithms in OpenSSL, but also in the other validated packages.
To have the application output a hash one would need to replace the input to digest_message() with a hash that is allowed in FIPS 140-2, for example, EVP_sha256()
.
Overriding FIPS
Prohibiting applications from using forbidden algorithms protects the user of the system from applications that are using cryptography not allowed by FIPS 140-2. However, there are cases where a cryptographic algorithm is used for non-cryptographic purposes. For example MD5 is used to compute an identifier that is used for backwards compatibility with an older protocol that is wrapped within a secure one (e.g., Radius over TLS).
In these cases a developer can override the internal checks, by using the flag EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
. That flag needs to be set just after the call to EVP_MD_CTX_new
as shown below.
if ((mdctx = EVP_MD_CTX_new()) == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_MD_CTX_set_flags(mdctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
if (1 != EVP_DigestInit_ex(mdctx, type, NULL)) {
ERR_print_errors_fp(stderr);
return -1;
}
Checking whether the application runs in FIPS mode
OpenSSL 1.1.1 provides the FIPS_mode()
function for checking whether the library operates in FIPS mode. As this function is no longer available on OpenSSL 3.0, a more generic way to check whether the application runs in FIPS mode is to check the file /proc/sys/crypto/fips_enabled
.
When the Ubuntu FIPS kernel is present and runs with FIPS enabled, the /proc/sys/crypto/fips_enabled
file exists and contains the 0x31 byte (character ‘1’ in ASCII). This in Ubuntu indicates that FIPS is enabled.
Summing up
Ubuntu enables developing and running applications that comply with the FIPS 140-2 data protection standard. The approach we follow gives a system-wide switch that is transparent for the applications and makes development easy by ensuring that applications that use non-compliant algorithms fail to execute. Learn more about developing with FIPS in our documentation pages, and use your Free personal subscription or your Ubuntu Advantage subscription to develop applications using the Ubuntu FIPS packages!
Talk to us today
Interested in running Ubuntu in your organisation?
Newsletter signup
Related posts
6 facts for CentOS users who are holding on
Considering migrating to Ubuntu from other Linux platforms, such as CentOS? Find six useful facts to get started!
Why is Ubuntu Linux the leading choice to replace CentOS for financial services?
Financial services are powered by technology. The customer experience is increasingly driven by data, with tailoring of products and services to reflect...
Ubuntu 22.04 FIPS 140-3 modules available for preview
Canonical has been working with our testing lab partner, atsec information security, to prepare the cryptographic modules in Ubuntu 22.04 LTS (Jammy...