If you work with US government entities or corporations in regulated markets the subject of FIPS compliance may come up, especially in the context of FedRAMP authorization. FIPS 140-2 and FIPS 140-3 are a set of cryptographic standards that your application may need to adhere to dictating the appropriate ciphers or cryptographic functions that are in use.
It can take a lot of effort to run a completely FIPS compliant architecture, but for application developers, the bottom line is that any code that uses cryptographic operations to secure customer data must use FIPS certified cryptographic libraries.
This article describes first what FIPS is and how cryptographic modules are validated as FIPS compliant, and follows up with guidance on making your application FIPS compliant.
Laying the Groundwork
What is FIPS?
FIPS stands for the Federal Information Processing Standard (FIPS), which is a joint US and Canadian government standard that specifies the security requirements for cryptographic modules that protect sensitive information.
When do I need FIPS?
The most likely scenario requiring FIPS is FedRAMP certification. Specifically, FedRAMP control SC-13 states that whenever cryptography is used to protect sensitive data (Controlled Unclassified Information), it must be FIPS-validated:
The following types of cryptography [are] required for each specified cryptographic use: FIPS-validated or NSA-approved cryptography.
What does FIPS-validated mean?
To become FIPS validated, a cryptographic module must be certified by the Cryptographic Module Validation Program (CMVP) of the NIST Computer Security Resource Center. Modules that have been certified are given a CMVP number.
FIPS for Developers
To become FedRAMP certified you will need to implement FIPS-validated cryptography wherever crypto operations are used to secure sensitive data. This may include encrypting data in transit, encrypting data at rest, implementing digital signatures or signed URLs, or hashing passwords using crypto modules.
FIPS Compliance for Java
Java provides a set of interfaces for performing cryptographic operations under Java Cryptography Architecture and Java Cryptography Extension. These interfaces provide a standardized way to configure the cryptography implementation and configuration for your application.
Two implementations of Java cryptographic algorithms are FIPS validated: Bouncy Castle and the Amazon Corretto Crypto Provider.
BouncyCastle is maintained by non-profit an Australian charity, the Legion of the Bouncy Castle, which makes FIPS variants available. Commercial support contracts are available for BouncyCastle and help support the project.
The Amazon Corretto Crypto Provider (ACCP) is based off of AWS-LC is a general-purpose cryptographic library maintained by the AWS cryptography team. Since AWS is likely the largest provider of FedRAMP compliant environments, they have a vested interest in making FIPS compliant cryptography available for their customers.
Using ACCP is as simple as including it as a dependency in your project
with the -FIPS
variant.
Installing ACCP-FIPS
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider-FIPS</artifactId>
<version>[2.0, 3.0)</version>
<classifier>linux-x86_64</classifier>
</dependency>
dependencies {
implementation 'software.amazon.cryptools:AmazonCorrettoCryptoProvider-FIPS:2.+:linux-x86_64'
}
Configuring ACCP-FIPS
There are several ways to configure the ACCP as the highest priority provider in Java.
Via Code
Run the following method early in program start up:
com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider.install()
Via Security Properties
Add ACCP as the preferred provider in java.security.properties
. This
involves writing a security.properties
file configuring the preferences
for provider:
# List of providers and their preference orders
security.provider.1=com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider
security.provider.2=SUN
security.provider.3=SunRsaSign
security.provider.4=SunEC
security.provider.5=SunJSSE
security.provider.6=SunJCE
security.provider.7=SunJGSS
security.provider.8=SunSASL
security.provider.9=XMLDSig
security.provider.10=SunPCSC
security.provider.11=JdkLDAP
security.provider.12=JdkSASL
security.provider.13=SunMSCAPI
security.provider.14=Apple
security.provider.15=SunPKCS11
securerandom.strongAlgorithms=DEFAULT:AmazonCorrettoCryptoProvider,NativePRNGBlocking:SUN,DRBG:SUN
And then adding this file as an option when running your application:
-Djava.security.properties=/path/to/amazon-corretto-crypto-provider.security
Via JVM settings
Modify the java.security
file provided by your JVM so that the highest
priority provider is the Amazon Corretto Crypto Provider.
You can download the sample file from Github.
FIPS Compliance for Go
Go’s native crypto implementation is not FIPS compliant, and there are no plans to change this:
Go’s crypto is not FIPS 140 validated and I’m afraid that there is no possibility of that happening in the future either. I think Ian’s suggestion of using cgo to call out to an existing, certified library is probably your best bet. However, we would not be interested in patches to add hook points all over the Go library, so you would need to carry that work yourself.
This has led to a situation where there are multiple different ways to achieve FIPS compliance by using alternate versions of the standard Go crypto implementation.
Provider | Library | Go Toolchain | Operating System | Github Repo |
---|---|---|---|---|
BoringCrypto | Standard | Linux | golang/go | |
RedHat | OpenSSL | RedHat | Linux | golang-fips/go |
Microsoft | OpenSSL | Microsoft | Linux | microsoft/go |
Microsoft | Windows CNG | Microsoft | Windows | microsoft/go |
The FIPS implementation most compatible with existing Go projects is
Google’s BoringCrypto implementation which bundles BoringCrypto into the
application and calls out to it using cgo interfaces. Since BoringCrypto
is integrated with the Go toolchain, enabling it is fairly simple: pass
the environment variable GOEXPERIMENT=boringcrypto
to the go tool
during build time. As simple as that.
# Build a binary that uses boringcrypto instead of the native golang crypto
% GOEXPERIMENT=boringcrypto go build
Instead of manually setting FIPS-complaint ciphers and TLS versions every time you use a crypto library, you can add the following file to your project to force using FIPS validated TLS and ciphers regardless of any runtime settings.
//go:build boringcrypto
// +build boringcrypto
package main
import _ "crypto/tls/fipsonly"
If you don’t need FIPS/non-FIPS selection at runtime this is simpler and more convenient.
If you are interested in diving deeper into alternative implementations of FIPS compliant Go, see the following blog posts:
FIPS Compliance for Rust
The easiest way to get FIPS compliance with Rust is to leverage
aws-lc through the
aws-lc-rs Rust library. When
configured to use FIPS, this library uses the auto-generated
aws-lc-fips-sys
Foreign Function Interface (FFI) to invoke
cryptographic operations using AWS-LC.
aws-lc-rs
intends to contribute a drop-in replacement for the popular
ring crate that provides FIPS
support and is compatible through the existing ring API. Rust developers
with prescribed cryptographic requirements can seamlessly integrate
aws-lc-rs
into their applications acheive FIPS compliance.
The Rust project contains three crates, two of which are relevant for FIPS:
- aws-lc-rs: A library using the cryptographic operations provided by AWS-LC using either aws-lc-sys or aws-lc-fips-sys.
- aws-lc-fips-sys: Low-level AWS-LC bindings for the Rust programming language, providing FIPS support.
aws-lc-rs
is available through crates.io and can be added to your
project in the standard way using Cargo.toml
and enable the fips
feature:
[dependencies]
aws-lc-rs = { version = "1.7.2", features = [ "fips" ] }
Once added, you can use aws-lc-rs through the same API as ring. API docs are available through docs.rs.
FIPS Compliance for Python
The cryptography package depends on the standard OpenSSL C library on the host OS for all cryptographic operations. OpenSSL is the de facto standard for cryptographic libraries and provides high performance along with various certifications that may be relevant to developers. It has also been tested against the BoringCrypto implementation.
If you run a FIPS compatible version of OpenSSL at the Linux host level,
cryptography operations in Python will inherit that compatabitily. You
can verify this to be true using the hazmat
package:
from cryptography.hazmat.primitives.fips import is_fips_mode_enabled
if is_fips_mode_enabled():
print("FIPS mode is enabled")
else:
print("FIPS mode is not enabled")
FIPS Compliance for OpenSSL and Linux Hosts
OpenSSL provides FIPS compatible variants that can be enabled at the host level. With this option enabled, all key generations and cryptographic operations are done with FIPS-approved algorithms.
The exact method of enabling FIPS mode varies depending on the Linux
variant you use, but the general steps are to install dracut-fips
and
use it to generate a FIPS compliant initial ram file system
(initramfs
). initramfs
is used to prepare Linux systems during boot
before the init process starts.
For example, on Amazon Linux 2, the following bash script will check if FIPS mode is enabled, and if it is not install the required FIPS packages, and configre the boot loader to run the system in FIPS mode:
#!/bin/bash
set -uex
if grep -q "fips=1" /etc/default/grub; then
echo "FIPS mode already enabled."
exit 0
fi
# The dracut-fips package provides the
# modules to build an initramfs file
# system that operates in FIPS mode.
yum install -y dracut-fips
# Regenerate initramfs
dracut -f
# Tell GRUB to start the system in FIPS mode.
# The following `sed` call adds `fips=1` to the end of the
# `GRUB_CMDLINE_LINUX_DEFAULT` line.
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="[^"]*/& fips=1/' /etc/default/grub
# Regenerate GRUB config
grub2-mkconfig -o /etc/grub2.cfg
For other operating systems, see the following guides:
FIPS Compliance for Container Environments
In general, cryptographic operations in a container use the host-supplied version of OpenSSL. This means that to run a FIPS compliant container, you must first run a FIPS validated kernel on the host. Installing cryptographic modules in your container will use the host supplied versions running in FIPS mode.
You can verify if FIPS is enabled by checking
/proc/sys/crypto/fips_enabled
:
$ cat /proc/sys/crypto/fips_enabled
1
If you need to install OpenSSL or other cryptographic modules on your container, make sure one of the following requirements is met:
- The
dracut-fips
package is installed in the container. - The
/etc/system-fips
file is mounted on the container from the host.
Summary
FIPS (Federal Information Processing Standard) is a set of security standards for cryptographic modules that protect sensitive information. Compliance with FIPS is often required for FedRAMP certification, especially when dealing with controlled unclassified information.
How to achieve FIPS compliance:
- Java: Use FIPS-validated implementations like Bouncy Castle or Amazon Corretto Crypto Provider (ACCP). ACCP can be easily added as a dependency and configured as the preferred provider.
- Go: While Go’s native crypto implementation isn’t FIPS compliant, you can achieve compliance by compiling your application using Google’s BoringCrypto implementation.
- Rust: Leverage the aws-lc-rs crate, which offers FIPS support through AWS-LC. It aims to provide a drop-in replacement for the ring crate with FIPS compliance.
- Python: If the host’s OpenSSL library is FIPS compliant, Python’s cryptography operations will inherit that compliance.
- OpenSSL and Linux Hosts: Enable FIPS mode at the host level to ensure FIPS-approved algorithms for key generations and cryptographic operations.
- Container Environments: Ensure the host runs a FIPS-validated
kernel and, in some cases, that the container either has the
dracut-fips
package or the/etc/system-fips
file mounted from the host.