1
0
mirror of https://git.code.sf.net/p/linux-ima/ima-evm-utils synced 2025-04-28 14:43:37 +02:00
ima-evm-utils-mirror/tests/sign_verify.test
Stefan Berger 9473e3c887 ima_evm_utils: Add testing with elliptic curves prime192v1 and 256v1
Add test cases that test the signing and signature verification with the
elliptic curves prime192v1 and prime256v1, also known as NIST P192 and
P256. These curves will soon be supported by Linux. If OpenSSL cannot
generate prime192v1 keys, as is the case on Fedora, where this curve is
not supported, the respective tests will be skipped automatically.

The r and s integer components of the signature can have varying size.
Therefore we do the size checks for the entire signature with a regular
expression that accounts for the varying size. The most typical cases
are supported following hours of running the tests with varying keys.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
2021-02-04 12:44:30 -05:00

403 lines
11 KiB
Bash
Executable File

#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# evmctl {,ima_}{sign,verify} tests
#
# Copyright (C) 2020 Vitaly Chikunov <vt@altlinux.org>
#
# 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 2, 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.
cd "$(dirname "$0")" || exit 1
PATH=../src:$PATH
source ./functions.sh
_require cmp evmctl getfattr openssl xxd
if cmp -b 2>&1 | grep -q "invalid option"; then
echo "cmp does not support -b (cmp from busybox?) Use cmp from diffutils"
exit "$HARDFAIL"
fi
./gen-keys.sh >/dev/null 2>&1
trap _report_exit EXIT
set -f # disable globbing
# Determine keyid from a cert
_keyid_from_cert() {
local cer=${1%.*}.cer cmd
local tmp
cer=test-${cer#test-}
# shellcheck disable=SC2086
cmd="openssl x509 $OPENSSL_ENGINE \
-in $cer -inform DER -pubkey -noout"
id=$($cmd 2>/dev/null \
| openssl asn1parse \
| grep BIT.STRING \
| tail -n1 \
| cut -d: -f1)
if [ -z "$id" ]; then
echo - "$cmd" >&2
echo "Cannot asn1parse $cer to determine keyid" >&2
exit 1
fi
tmp=$(mktemp)
# shellcheck disable=SC2086
openssl x509 $OPENSSL_ENGINE \
-in "$cer" -inform DER -pubkey -noout 2>/dev/null \
| openssl asn1parse -strparse "$id" -out "$tmp" -noout
# shellcheck disable=SC2002
cat "$tmp" \
| openssl dgst -c -sha1 \
| cut -d' ' -f2 \
| grep -o ":..:..:..:..$" \
| tr -d :
rm -f "$tmp"
}
# Convert test $type into evmctl op prefix
_op() {
if [ "$1" = ima ]; then
echo ima_
fi
}
# Convert test $type into xattr name
_xattr() {
if [ "$1" = ima ]; then
echo user.ima
else
echo user.evm
fi
}
# Check that detached signature matches xattr signature
_test_sigfile() {
local file=$1 attr=$2 file_sig=$3 file_sig2=$4
if [ ! -e "$file_sig" ]; then
color_red
echo "evmctl ima_sign: no detached signature $file_sig"
color_restore
rm "$file"
return "$FAIL"
fi
_extract_xattr "$file" "$attr" "$file_sig2"
if ! cmp -bl "$file_sig" "$file_sig2"; then
color_red
echo "evmctl ima_sign: xattr signature on $file differ from detached $file_sig"
color_restore
rm "$file" "$file_sig" "$file_sig2"
return "$FAIL"
fi
# Leave '$file_sig' for ima_verify --sigfile test.
rm "$file_sig2"
}
# Run single sign command
_evmctl_sign() {
local type=$1 key=$2 alg=$3 file=$4 opts=$5
# Can check --sigfile for ima_sign
[ "$type" = ima ] && opts+=" --sigfile"
# shellcheck disable=SC2086
ADD_TEXT_FOR="$alg ($key)" ADD_DEL=$file \
_evmctl_run "$(_op "$type")sign" $opts \
--hashalgo "$alg" --key "$key" --xattr-user "$file" || return
if [ "$type" = ima ]; then
_test_sigfile "$file" "$(_xattr "$type")" "$file.sig" "$file.sig2"
fi
}
# Run and test {ima_,}sign operation
check_sign() {
# Arguments are passed via global vars:
# TYPE (ima or evm),
# KEY,
# ALG (hash algo),
# PREFIX (signature header prefix in hex),
# OPTS (additional options for evmctl),
# FILE (working file to sign).
local "$@"
local KEY=${KEY%.*}.key
local FILE=${FILE:-$ALG.txt}
# Normalize key filename
KEY=test-${KEY#test-}
# Append suffix to files for negative tests, because we may
# leave only good files for verify tests.
_test_expected_to_fail && FILE+='~'
rm -f $FILE
if ! touch $FILE; then
color_red
echo "Can't create test file: $FILE"
color_restore
return "$HARDFAIL"
fi
if _test_expected_to_pass; then
# Can openssl work with this digest?
cmd="openssl dgst $OPENSSL_ENGINE -$ALG $FILE"
echo - "$cmd"
if ! $cmd >/dev/null; then
echo "${CYAN}$ALG ($KEY) test is skipped (openssl is unable to digest)$NORM"
return "$SKIP"
fi
if [ ! -e "$KEY" ]; then
echo "${CYAN}$ALG ($KEY) test is skipped (key file not found)$NORM"
return "$SKIP"
fi
# Can openssl sign with this digest and key?
cmd="openssl dgst $OPENSSL_ENGINE -$ALG -sign $KEY -hex $FILE"
echo - "$cmd"
if ! $cmd >/dev/null; then
echo "${CYAN}$ALG ($KEY) test is skipped (openssl is unable to sign)$NORM"
return "$SKIP"
fi
fi
# Insert keyid from cert into PREFIX in-place of marker `:K:'
if [[ $PREFIX =~ :K: ]]; then
keyid=$(_keyid_from_cert "$KEY")
if [ $? -ne 0 ]; then
color_red
echo "Unable to determine keyid for $KEY"
color_restore
return "$HARDFAIL"
fi
[ "$VERBOSE" -gt 2 ] && echo " Expected keyid: $keyid"
PREFIX=${PREFIX/:K:/$keyid}
fi
# Perform signing by evmctl
_evmctl_sign "$TYPE" "$KEY" "$ALG" "$FILE" "$OPTS" || return
# First simple pattern match the signature.
ADD_TEXT_FOR=$ALG \
_test_xattr "$FILE" "$(_xattr "$TYPE")" "$PREFIX.*" || return
# This is all we can do for v1 signatures.
[[ "$OPTS" =~ --rsa ]] && return "$OK"
# This is all we can do for evm.
[[ "$TYPE" =~ evm ]] && return "$OK"
# Extract signature to a file
_extract_xattr "$FILE" "$(_xattr "$TYPE")" "$FILE.sig2" "$PREFIX"
# Verify extracted signature with openssl
cmd="openssl dgst $OPENSSL_ENGINE -$ALG -verify ${KEY%.*}.pub \
-signature $FILE.sig2 $FILE"
echo - "$cmd"
if ! $cmd; then
color_red_on_failure
echo "Signature v2 verification with openssl is failed."
color_restore
rm "$FILE.sig2"
return "$FAIL"
fi
rm "$FILE.sig2"
return "$OK"
}
# Test verify operation
check_verify() {
# Arguments are passed via global vars:
# TYPE (ima or evm),
# KEY,
# ALG (hash algo),
# OPTS (additional options for evmctl),
# FILE (filename to verify).
local "$@"
# shellcheck disable=SC2086
if ! openssl dgst $OPENSSL_ENGINE -"$ALG" /dev/null >/dev/null 2>&1; then
echo $CYAN"$ALG ($KEY) test is skipped (openssl does not support $ALG)"$NORM
return $SKIP
fi
# shellcheck disable=SC2086
ADD_TEXT_FOR="$FILE ($KEY)" \
_evmctl_run "$(_op "$TYPE")verify" --key "$KEY" --xattr-user $OPTS "$FILE"
}
# Test runners
# Perform sign and verify ima and evm testing
sign_verify() {
local key=$1 alg=$2 prefix="$3" opts="$4"
local file=$alg.txt
# Set defaults:
# Public key is different for v1 and v2 (where x509 cert is used).
if [[ $opts =~ --rsa ]]; then
KEY=test-$key.pub
else
KEY=test-$key.cer
fi
ALG=$alg
PREFIX=$prefix
OPTS=$opts
FILE=$file
TYPE=ima
if expect_pass check_sign; then
# Normal verify with proper key should pass
expect_pass check_verify
expect_pass check_verify OPTS="--sigfile"
# Multiple files and some don't verify
expect_fail check_verify FILE="/dev/null $file"
rm "$FILE.sig"
fi
TYPE=evm
# Avoid running blkid for evm tests which may require root
# No generation on overlayfs:
# ioctl(3, FS_IOC_GETVERSION, 0x7ffd8e0bd628) = -1 ENOTTY (Inappropriate ioctl for device)
OPTS="$opts --uuid --generation 0"
if expect_pass check_sign; then
# Normal verify with proper key
expect_pass check_verify
# Verify with wrong key
expect_fail check_verify KEY=rsa2048
fi
# Note: Leaving TYPE=evm and file is evm signed
}
# Test --keys
try_different_keys() {
# This run after sign_verify which leaves
# TYPE=evm and file is evm signed
# v2 signing can work with multiple keys in --key option
if [[ ! $OPTS =~ --rsa ]]; then
# Have correct key in the key list
expect_pass check_verify KEY="test-rsa2048.cer,$KEY"
expect_pass check_verify KEY="/dev/null,$KEY,"
fi
# Try key that is not used for signing
expect_fail check_verify KEY=rsa2048
# Try completely wrong key files
expect_fail check_verify KEY=/dev/null
expect_fail check_verify KEY=/dev/zero
}
try_different_sigs() {
# TYPE=evm and file is evm signed
# Test --imasig
if expect_pass check_sign OPTS="$OPTS --imasig"; then
# Verify both evm and ima sigs
expect_pass check_verify
expect_pass check_verify TYPE=ima
fi
# Test --imahash
if expect_pass check_sign OPTS="$OPTS --imahash"; then
expect_pass check_verify
# IMA hash is not verifiable by ima_verify
expect_fail check_verify TYPE=ima
fi
# Test --portable (only supported for V2 signatures)
if expect_pass check_sign OPTS="$OPTS --portable --imahash" PREFIX=0x05; then
if [[ "$OPTS" =~ --rsa ]]; then
expect_fail check_verify
else
expect_pass check_verify
fi
fi
# Test -i (immutable)
expect_pass check_sign OPTS="$OPTS -i" PREFIX=0x0303
# Cannot be verified for now
}
# Single test args: type key hash signature-prefix "evmctl-options"
# sign_verify args: key hash signature-prefix "evmctl-options"
# Only single test can be prefixed with expect_{fail,pass}
# `sign_verify' can not be prefixed with expect_{fail,pass} because
# it runs multiple tests inside. See more tests there.
# signature-prefix can contain `:K:' which will be resolved to keyid (v2 only)
## Test v1 signatures
# Signature v1 only supports sha1 and sha256 so any other should fail
expect_fail \
check_sign TYPE=ima KEY=rsa1024 ALG=md5 PREFIX=0x0301 OPTS=--rsa
sign_verify rsa1024 sha1 0x0301 --rsa
sign_verify rsa1024 sha256 0x0301 --rsa
try_different_keys
try_different_sigs
## Test v2 signatures with RSA PKCS#1
# List of allowed hashes much greater but not all are supported.
sign_verify rsa1024 md5 0x030201:K:0080
sign_verify rsa1024 sha1 0x030202:K:0080
sign_verify rsa1024 sha224 0x030207:K:0080
sign_verify rsa1024 sha256 0x030204:K:0080
try_different_keys
try_different_sigs
sign_verify rsa1024 sha384 0x030205:K:0080
sign_verify rsa1024 sha512 0x030206:K:0080
sign_verify rsa1024 rmd160 0x030203:K:0080
# Test v2 signatures with ECDSA
# Signature length is typically 0x34-0x38 bytes long, very rarely 0x33
sign_verify prime192v1 sha1 0x030202:K:003[345678]
sign_verify prime192v1 sha224 0x030207:K:003[345678]
sign_verify prime192v1 sha256 0x030204:K:003[345678]
sign_verify prime192v1 sha384 0x030205:K:003[345678]
sign_verify prime192v1 sha512 0x030206:K:003[345678]
# Signature length is typically 0x44-0x48 bytes long, very rarely 0x43
sign_verify prime256v1 sha1 0x030202:K:004[345678]
sign_verify prime256v1 sha224 0x030207:K:004[345678]
sign_verify prime256v1 sha256 0x030204:K:004[345678]
sign_verify prime256v1 sha384 0x030205:K:004[345678]
sign_verify prime256v1 sha512 0x030206:K:004[345678]
# Test v2 signatures with EC-RDSA
_enable_gost_engine
sign_verify gost2012_256-A md_gost12_256 0x030212:K:0040
sign_verify gost2012_256-B md_gost12_256 0x030212:K:0040
sign_verify gost2012_256-C md_gost12_256 0x030212:K:0040
sign_verify gost2012_512-A md_gost12_512 0x030213:K:0080
sign_verify gost2012_512-B md_gost12_512 0x030213:K:0080
# Test if signing with wrong key length does not work.
expect_fail \
check_sign TYPE=ima KEY=gost2012_512-B ALG=md_gost12_256 PREFIX=0x0302 OPTS=
expect_fail \
check_sign TYPE=ima KEY=gost2012_256-B ALG=md_gost12_512 PREFIX=0x0302 OPTS=