Tuesday, December 5, 2017

OpenSSL FIPS 140-2 - Part Two - Unix OS

Compiling OpenSSL with the FIPS module on Unix-ish operating systems like Linux and Cygwin is pretty straightforward.

I was not able to correctly build the FIPS module on 64-bit Cygwin. There’s a bug in the ./config script that prevents it from compiling. I could get around the issue by running ./Configure linux-generic64 instead of the standard ./config script, but that would violate the mandated build procedure. The 32-bit version of Cygwin compiles everything without issue.

If you’d like to use the Cygwin compiled openssl.exe binary on a Windows system without Cygwin installed, copy the cygwin1.dll and cyggcc_s-1.dll files from the Cygwin bin directory and put it in the same directory as the openssl.exe file. Of course, it makes more sense to simply use the native Windows OS build.


Build instructions


The official Linux build instructions for the FIPS module and OpenSSL can be found in section 4.2 of the User Guide for the OpenSSL FIPS Object Module v2.0. I will summarize the process here.
  1. Download the following files: openssl-1.0.2s.tar.gz and openssl-fips-2.0.16.tar.gz. Remember, you can only have a validated build by getting the FIPS source code by mail.
  2. Extract the tar files:
  3. $ tar -zxvf openssl-fips-2.0.16.tar.gz
    $ tar -zxvf openssl-1.0.2s.tar.gz
    
    Both source code packages include a top level directory with the same name as the tarballs.
  4. Perform the FIPS build exactly as specified. (You may use ./config no-­asm to avoid using the assembler, but that’s the only allowed option.)
  5. $ cd openssl-fips-2.0.16
    $ ./config
    $ make
    $ make install
    $ cd ..
    The FIPS binaries and libraries will be installed to the /usr/local/ssl/fips-­2.0 directory.
  6. We now can build OpenSSL with FIPS.
  7. $ cd openssl-1.0.2s
    $ ./config fips --with-fipsdir=/usr/local/ssl/fips-2.0
    $ make depend
    $ make
    $ cd apps
    Do not perform a “make install” after it’s been compiled. We don’t want to overwrite the existing OpenSSL binary that might be installed as part of the operating system. The resulting OpenSSL binary will be in the apps subdirectory.
  8. Verify the build.
  9. $ ./openssl version -a
    It should return something like:
    OpenSSL 1.0.2s-fips  28 May 2019
    built on: reproducible build, date unspecified
    platform: linux-x86_64
    options:  ...
    compiler: ...
    OPENSSLDIR: "/usr/local/ssl"
    Don’t worry if you get a warning about not being able to read the configuration file.

Commands for encrypting/decrypting files


Encrypt:
openssl aes-256-cbc -salt -md sha256 -e -in filename.ext -out filename.ext.enc

Decrypt:
openssl aes-256-cbc -md sha256 -d -in filename.ext.enc -out filename.ext

I made a bash script called sslenc.sh to make this even easier:

#!/bin/bash
 
# Functions
 
function getExecDir ()
{
  SOURCE="${BASH_SOURCE[0]}"
  # resolve $SOURCE until the file is no longer a symlink
  while [ -h "$SOURCE" ]; do
    DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
    SOURCE="$(readlink "$SOURCE")"
    # if $SOURCE was a relative symlink, we need to 
    #   resolve it relative to the path where the symlink
    #   file was located
    [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
  done
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
}
 
function usage ()
{
echo "Usage: $0 [FILE]..." 1>&2
echo "Encrypt/decrypt file using openssl." 1>&2
exit 1
}
 
function getPasswd ()
{
  # Define encryption password
  if [ -z "$PASSWD" ]
  then
    echo -n "Enter $CIPHER encryption password: "
    stty -echo
    read -r PASSWD
    stty echo
    echo
  fi
}
 
function checkFIPS ()
{
  if [ -f "$OPENSSL_EXEC" ]
  then
    FIPS="$($OPENSSL_EXEC version|grep fips && echo 1 || echo 0)"
 
    if [ "$FIPS" == "0" ]
    then
      echo "OpenSSL executable not FIPS build." 1>&2
      exit 1
    fi
  else
    echo "OpenSSL executable not found." 1>&2
    exit 1
  fi
}
 
# Get directory the bash script has been executed from and store in $DIR
getExecDir
 
# Global variables
CIPHER="aes-256-cbc"
OPENSSL_EXEC="$DIR/openssl"
export OPENSSL_CONF=/dev/null
PASSWD=""
 
# Provide usage information if no parameters provided
if [ "$#" == "0" ]
then
  usage
fi
 
# Ensure OpenSSL compiled with FIPS
checkFIPS
 
# Process all command line parameters
while (( "$#" ))
do
 
  # Check if regular file
  if [ -f "$1" ]
  then
 
    # File is encrypted if first 8 bytes is "Salted__"
    if [ "$(head -c 8 "$1"| tr -d \\0)" == "Salted__" ]
    then
 
      # Ensure encrypted filenames end in ".enc"
      if [ "${1: -4}" == ".enc" ]
      then
 
        # Check if output file exists
        if [ -f "${1%.enc}" ]
        then
          echo -n "File exists: ${1%.enc} Overwrite? "
          read -r ANSWER
          case $ANSWER in
            [yY])
              echo "Overwriting file..." 
            ;;
            *) echo "Skipping..."
              shift
              continue
            ;;
          esac
        fi 
 
        getPasswd
        $OPENSSL_EXEC $CIPHER -md sha256 -d -in "$1" -out "${1%.enc}" -pass pass:"$PASSWD"
      else
        echo "Encrypted filename \"$1\" must end in \".enc\" Skipping..." 1>&2
      fi
 
    else
 
      # Check if output file exists
      if [ -f "$1.enc" ]
      then
        echo -n "File exists: $1.enc Overwrite? "
        read -r ANSWER
        case $ANSWER in
          [yY])
            echo "Overwriting file..." 
          ;;
          *) echo "Skipping..."
            shift
            continue
          ;;
        esac
      fi 
 
      getPasswd
      $OPENSSL_EXEC $CIPHER -salt -md sha256 -e -in "$1" -out "$1.enc" -pass pass:"$PASSWD"
    fi
 
  else
    echo "\"$1\" not a file. Skipping..." 1>&2
  fi
 
  shift
 
done

Put it in the same directory as the OpenSSL binary.

Encrypt:
sslenc.sh filename1.ext [filename2.ext ...]

Decrypt:
sslenc.sh filename1.ext.enc [filename2.ext.enc ...]

No comments:

Post a Comment