#!/bin/bash -ex # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: # # Copyright (c) 2019 Red Hat, Inc. # Author: Sergio Correia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # . tang-common-test-functions error() { echo "${1}" >&2 exit 1 } skip_test() { echo "${1}" >&2 exit 77 } # We require cryptsetup >= 2.0.4 to fully support LUKSv2. # Support is determined at build time. luks2_supported() { return @OLD_CRYPTSETUP@ } # Creates a new LUKS1 or LUKS2 device to be used. new_device() { local LUKS="${1}" local DEV="${2}" local PASS="${3}" # Some builders fail if the cryptsetup steps are not ran as root, so let's # skip the test now if not running as root. if [ "$(id -u)" != 0 ]; then skip_test "WARNING: You must be root to run this test; test skipped." fi # Using a default password, if none has been provided. if [ -z "${PASS}" ]; then PASS="${DEFAULT_PASS}" fi local DEV_CACHED="${TMP}/${LUKS}.cached" # Let's reuse an existing device, if there is one. if [ -f "${DEV_CACHED}" ]; then echo "Reusing cached ${LUKS} device..." cp -f "${DEV_CACHED}" "${DEV}" return 0 fi fallocate -l64M "${DEV}" cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \ --pbkdf-force-iterations 1000 --batch-mode \ --force-password "${DEV}" <<< "${PASS}" # Caching the just-formatted device for possible reuse. cp -f "${DEV}" "${DEV_CACHED}" } # Creates a new LUKS1 or LUKS2 device to be used, using a keyfile. new_device_keyfile() { local LUKS="${1}" local DEV="${2}" local KEYFILE="${3}" # Some builders fail if the cryptsetup steps are not ran as root, so let's # skip the test now if not running as root. if [ "$(id -u)" != 0 ]; then skip_test "WARNING: You must be root to run this test; test skipped." fi if [[ -z "${KEYFILE}" ]] || [[ ! -f "${KEYFILE}" ]]; then error "Invalid keyfile (${KEYFILE})." fi fallocate -l64M "${DEV}" cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \ --pbkdf-force-iterations 1000 --batch-mode \ "${DEV}" "${KEYFILE}" } pin_cfg_equal() { # Let's remove the single quotes from the pin configuration. local cfg1="${1//\'/}" local cfg2="${2//\'/}" # Now we sort and present them in compact form. local sorted_cfg1 sorted_cfg2 sorted_cfg1="$(jq --compact-output --sort-keys . < <(echo -n "${cfg1}"))" sorted_cfg2="$(jq --compact-output --sort-keys . < <(echo -n "${cfg2}"))" # And we finally compare. if [ "${sorted_cfg1}" = "${sorted_cfg2}" ]; then return 0 fi return 1 } compare_luks_header() { DEV1="${1}" DEV2="${2}" TMP="${3}" cryptsetup luksHeaderBackup "${DEV1}" \ --header-backup-file "${TMP}"/check-header1 cryptsetup luksHeaderBackup "${DEV2}" \ --header-backup-file "${TMP}"/check-header2 local cs1 cs2 cs1=$(cksum "${TMP}"/check-header1 | cut -d' ' -f 1) cs2=$(cksum "${TMP}"/check-header2 | cut -d' ' -f 1) rm -f "${TMP}"/check-header{1,2} if [ "${cs1}" == "${cs2}" ]; then return 0 fi return 1 } used_luks1_metadata_slots() { DEV="${1}" if ! luksmeta test -d "${DEV}"; then echo "" return 0 fi local clevis_uuid="cb6e8904-81ff-40da-a84a-07ab9ab5715e" luksmeta show -d "${DEV}" \ | sed -rn "s|^([0-9]+)\s+active\s+${clevis_uuid}$|\1|p" \ | tr '\n' ' ' | sed 's/ $//' } used_luks2_metadata_slots() { DEV="${1}" cryptsetup luksDump "${DEV}" \ | grep -E -A1 "^\s+[0-9]+:\s+clevis$" \ | sed -rn 's|^\s+Keyslot:\s+([0-9]+)$|\1|p' | sort -n \ | tr '\n' ' ' | sed 's/ $//' } used_luks2_metadata_tokens() { DEV="${1}" cryptsetup luksDump "${DEV}" \ | grep -E -B1 "^\s+Keyslot:\s+[0-9]+$" \ | sed -rn 's|^\s+([0-9]+): clevis|\1|p' \ | tr '\n' ' ' | sed 's/ $//' } compare_luks1_metadata() { DEV1="${1}" DEV2="${2}" # If both are non-initialized, metadata is the same. ! luksmeta test -d "${DEV1}" && ! luksmeta test -d "${DEV2}" && return 0 # Otherwise, metadata differ. ! luksmeta test -d "${DEV1}" && return 1 ! luksmeta test -d "${DEV2}" && return 1 local slt1 slt2 slt1=$(used_luks1_metadata_slots "${DEV1}") slt2=$(used_luks1_metadata_slots "${DEV2}") if [ "${slt1}" != "${slt2}" ]; then echo "used slots did not match ($slt1) ($slt2)" >&2 return 1 fi local slt md1 md2 for slt in ${slt1}; do md1="$(luksmeta load -d "${DEV}" -s "${slt}")" md2="$(luksmeta load -d "${DEV2}" -s "${slt}")" if [ "${md1}" != "${md2}" ]; then echo "metadata in slot ${slt} did not match" >&2 return 1 fi done return 0 } compare_luks2_metadata() { DEV1="${1}" DEV2="${2}" local slt1 slt2 slt1=$(used_luks2_metadata_slots "${DEV1}") slt2=$(used_luks2_metadata_slots "${DEV2}") if [ "${slt1}" != "${slt2}" ]; then echo "used slots did not match ($slt1) ($slt2)" >&2 return 1 fi local tkn1 tkn2 tkn1=$(used_luks2_metadata_tokens "${DEV1}") tkn2=$(used_luks2_metadata_tokens "${DEV2}") if [ "${tkn1}" != "${tkn2}" ]; then echo "used tokens did not match ($tkn1) ($tkn2)" >&2 return 1 fi local tkn md1 md2 for tkn in ${tkn1}; do md1="$(cryptsetup token export --token-id "${tkn}" "${DEV1}")" md2="$(cryptsetup token export --token-id "${tkn}" "${DEV2}")" if [ "${md1}" != "${md2}" ]; then echo "metadata in token ${tkn} did not match" >&2 return 1 fi done return 0 } new_passphrase() { jose jwk gen --input='{"kty":"oct","bytes":8}' --output=- \ | jose fmt --json=- --object --get k --unquote=- } export DEFAULT_PASS=' just-some- test-password-here 1.+?~!@#$%^&*();:'"'"'"[]{}_=/`\ '