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.

ProviderLibraryGo ToolchainOperating SystemGithub Repo
GoogleBoringCryptoStandardLinuxgolang/go
RedHatOpenSSLRedHatLinuxgolang-fips/go
MicrosoftOpenSSLMicrosoftLinuxmicrosoft/go
MicrosoftWindows CNGMicrosoftWindowsmicrosoft/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:

  1. aws-lc-rs: A library using the cryptographic operations provided by AWS-LC using either aws-lc-sys or aws-lc-fips-sys.
  2. 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.