FIPS Build Guidelines
FIPS Module Build
The FIPS module is the binary file fipscanister.o (fipscanister.lib for Windows) <ref>Technically speaking the FIPS module alsi includes the companion fipscanister.o.sha1,fips_premain.c, and fips_premain.c.sha1 files. </ref> created by the build process.
The Security Policy document defines a very specific procedure for creating a binary FIPS module from the official source distributions:
gunzip -c openssl-fips-2.0.N.tar.gz | tar xf - cd openssl-fips-2.0.N ./config make
(for Unix/Linux). The Security Policy and NIST CMVP web site entry also clearly state that the source distribution (tarball) cannot be modified, at all. The unofficial FIPS user Guide also notes this reestriction.
That is worth repeating as the question appears repeatedly: you cannot modify the contents of the official tarball. The CMVP has imposed that very specific prohibition on all of the OpenSSL FIPS Object Module validations. From the software developer/engineer perspective that prohibition is senseless and furstrating, but it is what it is. You can't modify the tarball, even if the tinest tweak would make the difference between success and failure in building the module for your platform of interest. The natural tendency of a software developer is to identify and implement the simplest and most elegant modification to fix a build or execution. That approach generally won't work when the goal is to claim FIPS 140-2 validated status for the resulting module.
Question: Okay, got it. The official canonical build commands given above must be used with an unmodified official tarball. The end result doesn't build a usable module for my platform if interest. Is there anything I can do?
The CMVP has provided specific guidance on some things that must be done (e.g. the canonical build commands) and some things that cannot be done (modification of the tarball contents). However, based on an extensive series of validations of specific platforms (Operational Environments in FIPS-speak) we can discern some general rules for a limited set of techniques that are presumptively permissible.
The ./config command determines a build target from various inputs, for example the uname command for Unix/Linux systems for native compilation. For cross-compilation the target characteristics are determined from environment variables MACHINE, SYSTEM, RELEASE, etc. The use of a shell script that sets the appropriate environment variables is well established. For instance http://opensslfoundation.com/fips/2.0/platforms/android/setenv-android-4.1.sh:
#!/bin/sh # Cross-compile environment for Android on ARMv7 # # This script assumes the Android NDK and the OpenSSL FIPS # tarballs have been unpacked in the same directory #android-sdk-linux/platforms Edit this to wherever you unpacked the NDK if [ -d android-ndk-r8b ]; then export ANDROID_NDK=$PWD/android-ndk-r8b fi if [ -d android-ndk-r8c ]; then export ANDROID_NDK=$PWD/android-ndk-r8c fi # Edit to reference the incore script (usually in ./util/) export FIPS_SIG=$PWD/openssl-fips-2.0.2/util/incore for i in linux darwin do if [ -d $ANDROID_NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/$i-x86/bin ]; then PATH=$ANDROID_NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/$i-x86/bin:$PATH fi done export PATH # # Shouldn't need to edit anything past here. # export MACHINE=armv7l export RELEASE=2.6.37 export SYSTEM=android export ARCH=arm export CROSS_COMPILE="arm-linux-androideabi-" export ANDROID_DEV="$ANDROID_NDK/platforms/android-14/arch-arm/usr" export HOSTCC=gcc
So in general specification of information needed to define the cross-compilation target for the toolkit is premissible, provided that the contents of the original official tarball are not modified.
Multiple validated platforms have also been cross-compiled with build utilities supplied and residing outside of the official source distribution workarea, e.g. http://opensslfoundation.com/fips/2.0/platforms/ios/setenv-ios-11.sh which contains:
# FIPS_SIG is the tool for determining the incore fingerprint #export FIPS_SIG=/usr/local/ssl/fingerprint-macho export FIPS_SIG="`pwd`"/iOS/incore_macho
The "incore" utilties (incore, incore6x, msincore, incore_macho) calculate and insert the incore digest of the executable file linked with the FIPS module, and are not used during creation of the actual FIPS module proper (fipscanister.o, fipscanister.lib). Note most openssl-fips-2.0.N.tar.gz tarballs contain one or more incore utilities; these cannot be modified in place. However, specification of separate utility files residing outside of the unpacked tarball is permissible.
Some build environment modifications are known to be disallowed.
The code path is an important concept. Modifications to the build environment that change the code path of the resulting binary code (e.g. adding/removing assembler optimizations, or changing between 32 and 64 bit code generation) mean that the original formally tested platform is no longer relevant.
Some limited modifications to the build environment are permissible provided that:
- the contents of the official tarball are not modified, at all
- the canonical build commands are used (gunzip ...; cd ...; ./config; make)
- the code path is the same as for a formally tested platform
Ok, if you created the FIPS module (the fipscanister.lib and technically also the fipscanister.lib.sha1, fips_premain.c, fips_premain.c.sha1 files) *exactly* as documented in the Security Policy and without *any* modification of the ./openssl-fips-2.0.3/ workarea, *then* you have a FIPS module you can claim as FIPS 140-2 validated.
Having achieved that the next step is to link that FIPS module into an executable application. Here the restrictions are far less severe; consisting essentially of two responsibilities:
1) Verify the digests of the FIPS module (fipscanister.o, fips_premain.c) against the *.sha1 files.
2) Set the integrity test digest. The fipsld utility does than for native compiled, and an "incore" utility for cross-compilation. Different "incore" utilities are used for various cross-compiled platforms.
Note the CMVP does not (to our knowledge) impose any specific requirement on the "fipsld" or "incore" utilities. While it can be very dangerous to presume an understanding of their thought processes, as they see FIPS 140-2 validation from a very different perspective than the typical software developer/engineer, I believe it goes something like this:
The integrity digest is verified at runtime as part of the mandated POST (Power Up Self Test, a key FIPS 140-2 concept). The code that performs that check is carefully and formally reviewed and tested. That integrity test consists of calculating a HMAC-SHA1 digest of the TXT and RODATA segments of the FIPS module as mapped in live memory, and comparing it against a known value embedded in the module. The "fipsld" or "incore" utility stores that known value. No formal testing is required for that utility because for given any fixed string of bits (i.e. the TXT+RODATA segments) there is only one possible correct value for the HMAC-SHA1 digest. If an untested and defective fipsld/incore utility stores an incorrect value then the POST will fail, therefore only the latter need be formally tested.