#!/usr/bin/env bash # fips-check.sh # This script checks the current revision of the code against the # previous release of the FIPS code. While wolfSSL and wolfCrypt # may be advancing, they must work correctly with the last tested # copy of our FIPS approved code. # # This should check out all the approved flavors. The command line # option selects the flavor. The keep option keeps the output # directory. # These variables may be overridden on the command line. MAKE="${MAKE:-make}" GIT="${GIT:-git -c advice.detachedHead=false}" TEST_DIR="${TEST_DIR:-XXX-fips-test}" case "$TEST_DIR" in /*) ;; *) TEST_DIR="${PWD}/${TEST_DIR}" ;; esac FLAVOR="${FLAVOR:-linux}" KEEP="${KEEP:-no}" MAKECHECK=${MAKECHECK:-yes} DOCONFIGURE=${DOCONFIGURE:-yes} DOAUTOGEN=${DOAUTOGEN:-yes} FIPS_REPO="${FIPS_REPO:-git@github.com:wolfssl/fips.git}" WOLFSSL_REPO="${WOLFSSL_REPO:-git@github.com:wolfssl/wolfssl.git}" Usage() { cat </dev/null then $GIT branch --no-track "my$tag" "$tag" || exit $? fi $GIT checkout "my$tag" -- "$name" || exit $? done } # copy_fips_files takes an array of pairs of file paths and git tags to # checkout. It will check to see if mytag exists and if now will make that # tag a branch. It breaks the filepath apart into file name and path, then # copies it from the file from the fips directory to the path. function copy_fips_files() { local name local bname local dname local tag for file_entry in "$@"; do name=${file_entry%%:*} tag=${file_entry#*:} bname=$(basename "$name") dname=$(dirname "$name") if ! $GIT rev-parse -q --verify "my$tag" >/dev/null; then $GIT branch --no-track "my$tag" "$tag" || exit $? fi $GIT checkout "my$tag" -- "$bname" || exit $? cp "$bname" "../$dname" done } # Note, it would be cleaner to compute the tag lists using associative arrays, # but those were introduced in bash-4. It's more important to maintain backward # compatibility here. declare -a WOLFCRYPT_TAGS_NEEDED_UNSORTED WOLFCRYPT_TAGS_NEEDED if [ ${#WOLFCRYPT_FILES[@]} -gt 0 ]; then for file_entry in "${WOLFCRYPT_FILES[@]}"; do WOLFCRYPT_TAGS_NEEDED_UNSORTED+=("${file_entry#*:}") done while IFS= read -r tag; do WOLFCRYPT_TAGS_NEEDED+=("$tag"); done < <(IFS=$'\n'; sort -u <<< "${WOLFCRYPT_TAGS_NEEDED_UNSORTED[*]}") if [ "${#WOLFCRYPT_TAGS_NEEDED[@]}" = "0" ]; then echo "Error -- missing wolfCrypt tags." 1>&2 exit 1 fi fi declare -a FIPS_TAGS_NEEDED_UNSORTED FIPS_TAGS_NEEDED for file_entry in "${FIPS_FILES[@]}"; do FIPS_TAGS_NEEDED_UNSORTED+=("${file_entry#*:}") done while IFS= read -r tag; do FIPS_TAGS_NEEDED+=("$tag"); done < <(IFS=$'\n'; sort -u <<< "${FIPS_TAGS_NEEDED_UNSORTED[*]}") if [ "${#FIPS_TAGS_NEEDED[@]}" = "0" ]; then echo "Error -- missing FIPS tags." 1>&2 exit 1 fi if [ ${#WOLFCRYPT_TAGS_NEEDED[@]} -gt 0 ]; then echo "wolfCrypt tag$( [[ ${#WOLFCRYPT_TAGS_NEEDED[@]} != "1" ]] && echo -n 's'):" # Only use shallow fetch if the repo already has shallow branches, to avoid # tainting full repos with shallow objects. if [ -f .git/shallow ]; then shallow_args=(--depth 1) else shallow_args=() fi for tag in "${WOLFCRYPT_TAGS_NEEDED[@]}"; do if $GIT describe --long --exact-match "$tag" 2>/dev/null; then continue fi if ! $GIT fetch "${shallow_args[@]}" "$WOLFSSL_REPO" tag "$tag"; then echo "Can't fetch wolfCrypt tag: $tag" 1>&2 exit 1 fi # Make sure the tag is associated: $GIT tag "$tag" FETCH_HEAD >/dev/null 2>&1 done fi if ! $GIT clone --shared . "$TEST_DIR"; then echo "fips-check: Couldn't clone current working directory." 1>&2 exit 1 fi # If there is a FIPS repo under the parent directory, leverage that: if [ -d ../fips/.git ]; then pushd ../fips 1>/dev/null || exit 2 # Only use shallow fetch if the repo already has shallow branches, to avoid # tainting full repos with shallow objects. if [ -f .git/shallow ]; then shallow_args=(--depth 1) else shallow_args=() fi echo "FIPS tag$( [[ ${#FIPS_TAGS_NEEDED[@]} != "1" ]] && echo -n 's'):" for tag in "${FIPS_TAGS_NEEDED[@]}"; do if [ "$tag" = "master" ]; then # master is handled specially below. continue fi if $GIT describe --long --exact-match "$tag" 2>/dev/null; then continue fi if ! $GIT fetch "${shallow_args[@]}" "$FIPS_REPO" tag "$tag"; then echo "Can't fetch FIPS tag: $tag" 1>&2 exit 1 fi # Make sure the tag is associated: $GIT tag "$tag" FETCH_HEAD >/dev/null 2>&1 done # The current tooling for the FIPS tests is in the master branch and must be # checked out here. if ! $GIT clone --shared --branch master . "${TEST_DIR}/fips"; then echo "fips-check: Couldn't clone current working directory." 1>&2 exit 1 fi popd 1>/dev/null || exit 2 # Make sure master is up-to-date: pushd "${TEST_DIR}/fips" 1>/dev/null || exit 2 if ! $GIT pull "$FIPS_REPO" master; then echo "Can't refresh master FIPS tag" 1>&2 exit 1 fi popd 1>/dev/null || exit 2 fi pushd "$TEST_DIR" 1>/dev/null || exit 2 if [ ! -d fips ]; then # The current tooling for the FIPS tests is in the master branch and must be # checked out here. if ! $GIT clone --depth 1 --branch master "$FIPS_REPO" fips; then echo "fips-check: Couldn't check out FIPS repository." exit 1 fi pushd fips 1>/dev/null || exit 2 echo "FIPS tag$( [[ ${#FIPS_TAGS_NEEDED[@]} != "1" ]] && echo -n 's'):" for tag in "${FIPS_TAGS_NEEDED[@]}"; do if [ "$tag" = "master" ]; then # master was just cloned fresh from $FIPS_REPO above. continue fi if $GIT describe --long --exact-match "$tag" 2>/dev/null; then continue fi # The FIPS repo here is an ephemeral clone, so we can safely use shallow # fetch unconditionally. if ! $GIT fetch --depth 1 "$FIPS_REPO" tag "$tag"; then echo "Can't fetch FIPS tag: $tag" 1>&2 exit 1 fi # Make sure the tag is associated: $GIT tag "$tag" FETCH_HEAD >/dev/null 2>&1 done popd 1>/dev/null || exit 2 fi checkout_files "${WOLFCRYPT_FILES[@]}" || exit 3 pushd fips 1>/dev/null || exit 2 copy_fips_files "${FIPS_FILES[@]}" || exit 3 popd 1>/dev/null || exit 2 # When checking out cert 3389 ready code, NIST will no longer perform # new certifications on 140-2 modules. If we were to use the latest files from # master that would require re-cert due to changes in the module boundary. # Since OE additions can still be processed for cert3389 we will call 140-2 # ready "fipsv2-OE-ready" indicating it is ready to use for an OE addition but # would not be good for a new certification effort with the latest files. if [ "$FLAVOR" = 'fipsv2-OE-ready' ] && [ -s wolfcrypt/src/fips.c ]; then cp wolfcrypt/src/fips.c wolfcrypt/src/fips.c.bak sed "s/v4.0.0-alpha/fipsv2-OE-ready/" wolfcrypt/src/fips.c.bak >wolfcrypt/src/fips.c fi # run the make test if [ "$DOAUTOGEN" = "yes" ]; then ./autogen.sh fi if [ "$DOCONFIGURE" = "yes" ]; then case "$FIPS_OPTION" in cavp-selftest) ./configure --enable-selftest ;; cavp-selftest-v2) ./configure --enable-selftest=v2 ;; *) ./configure --enable-fips=$FIPS_OPTION ;; esac if ! $MAKE; then echo 'fips-check: Make failed. Debris left for analysis.' exit 3 fi if [ -s wolfcrypt/src/fips_test.c ]; then NEWHASH=$(./wolfcrypt/test/testwolfcrypt | sed -n 's/hash = \(.*\)/\1/p') if [ -n "$NEWHASH" ]; then cp wolfcrypt/src/fips_test.c wolfcrypt/src/fips_test.c.bak sed "s/^\".*\";/\"${NEWHASH}\";/" wolfcrypt/src/fips_test.c.bak >wolfcrypt/src/fips_test.c make clean fi fi if [ "$MAKECHECK" = "yes" ]; then if ! $MAKE check; then echo 'fips-check: Test failed. Debris left for analysis.' exit 3 fi fi fi # Clean up popd 1>/dev/null || exit 2 if [ "$KEEP" = 'no' ]; then rm -rf "$TEST_DIR" fi