1*f93bc869SChristian Brauner#!/bin/bash 2*f93bc869SChristian Brauner# SPDX-License-Identifier: GPL-2.0 3*f93bc869SChristian Brauner# 4*f93bc869SChristian Brauner# Test script for dm-verity keyring functionality 5*f93bc869SChristian Brauner# 6*f93bc869SChristian Brauner# This script has two modes depending on kernel configuration: 7*f93bc869SChristian Brauner# 8*f93bc869SChristian Brauner# 1. keyring_unsealed=1 AND require_signatures=1: 9*f93bc869SChristian Brauner# - Upload a test key to the .dm-verity keyring 10*f93bc869SChristian Brauner# - Seal the keyring 11*f93bc869SChristian Brauner# - Create a dm-verity device with a signed root hash 12*f93bc869SChristian Brauner# - Verify signature verification works 13*f93bc869SChristian Brauner# 14*f93bc869SChristian Brauner# 2. keyring_unsealed=0 (default) OR require_signatures=0: 15*f93bc869SChristian Brauner# - Verify the keyring is already sealed (if unsealed=0) 16*f93bc869SChristian Brauner# - Verify keys cannot be added to a sealed keyring 17*f93bc869SChristian Brauner# - Verify the keyring is inactive (not used for verification) 18*f93bc869SChristian Brauner# 19*f93bc869SChristian Brauner# Requirements: 20*f93bc869SChristian Brauner# - Root privileges 21*f93bc869SChristian Brauner# - openssl 22*f93bc869SChristian Brauner# - veritysetup (cryptsetup) 23*f93bc869SChristian Brauner# - keyctl (keyutils) 24*f93bc869SChristian Brauner 25*f93bc869SChristian Braunerset -e 26*f93bc869SChristian Brauner 27*f93bc869SChristian BraunerWORK_DIR="" 28*f93bc869SChristian BraunerDATA_DEV="" 29*f93bc869SChristian BraunerHASH_DEV="" 30*f93bc869SChristian BraunerDM_NAME="verity-test-$$" 31*f93bc869SChristian BraunerCLEANUP_DONE=0 32*f93bc869SChristian Brauner 33*f93bc869SChristian Brauner# Module parameters (detected at runtime) 34*f93bc869SChristian BraunerKEYRING_UNSEALED="" 35*f93bc869SChristian BraunerREQUIRE_SIGNATURES="" 36*f93bc869SChristian Brauner 37*f93bc869SChristian Brauner# Colors for output 38*f93bc869SChristian BraunerRED='\033[0;31m' 39*f93bc869SChristian BraunerGREEN='\033[0;32m' 40*f93bc869SChristian BraunerYELLOW='\033[1;33m' 41*f93bc869SChristian BraunerNC='\033[0m' # No Color 42*f93bc869SChristian Brauner 43*f93bc869SChristian Braunerlog_info() { 44*f93bc869SChristian Brauner echo -e "${GREEN}[INFO]${NC} $*" 45*f93bc869SChristian Brauner} 46*f93bc869SChristian Brauner 47*f93bc869SChristian Braunerlog_warn() { 48*f93bc869SChristian Brauner echo -e "${YELLOW}[WARN]${NC} $*" 49*f93bc869SChristian Brauner} 50*f93bc869SChristian Brauner 51*f93bc869SChristian Braunerlog_error() { 52*f93bc869SChristian Brauner echo -e "${RED}[ERROR]${NC} $*" >&2 53*f93bc869SChristian Brauner} 54*f93bc869SChristian Brauner 55*f93bc869SChristian Braunerlog_pass() { 56*f93bc869SChristian Brauner echo -e "${GREEN}[PASS]${NC} $*" 57*f93bc869SChristian Brauner} 58*f93bc869SChristian Brauner 59*f93bc869SChristian Braunerlog_fail() { 60*f93bc869SChristian Brauner echo -e "${RED}[FAIL]${NC} $*" >&2 61*f93bc869SChristian Brauner} 62*f93bc869SChristian Brauner 63*f93bc869SChristian Braunerlog_skip() { 64*f93bc869SChristian Brauner echo -e "${YELLOW}[SKIP]${NC} $*" 65*f93bc869SChristian Brauner} 66*f93bc869SChristian Brauner 67*f93bc869SChristian Braunercleanup() { 68*f93bc869SChristian Brauner if [ "$CLEANUP_DONE" -eq 1 ]; then 69*f93bc869SChristian Brauner return 70*f93bc869SChristian Brauner fi 71*f93bc869SChristian Brauner CLEANUP_DONE=1 72*f93bc869SChristian Brauner 73*f93bc869SChristian Brauner log_info "Cleaning up..." 74*f93bc869SChristian Brauner 75*f93bc869SChristian Brauner # Remove dm-verity device if it exists 76*f93bc869SChristian Brauner if dmsetup info "$DM_NAME" &>/dev/null; then 77*f93bc869SChristian Brauner dmsetup remove "$DM_NAME" 2>/dev/null || true 78*f93bc869SChristian Brauner fi 79*f93bc869SChristian Brauner 80*f93bc869SChristian Brauner # Detach loop devices 81*f93bc869SChristian Brauner if [ -n "$DATA_DEV" ] && [[ "$DATA_DEV" == /dev/loop* ]]; then 82*f93bc869SChristian Brauner losetup -d "$DATA_DEV" 2>/dev/null || true 83*f93bc869SChristian Brauner fi 84*f93bc869SChristian Brauner if [ -n "$HASH_DEV" ] && [[ "$HASH_DEV" == /dev/loop* ]]; then 85*f93bc869SChristian Brauner losetup -d "$HASH_DEV" 2>/dev/null || true 86*f93bc869SChristian Brauner fi 87*f93bc869SChristian Brauner 88*f93bc869SChristian Brauner # Remove work directory 89*f93bc869SChristian Brauner if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then 90*f93bc869SChristian Brauner rm -rf "$WORK_DIR" 91*f93bc869SChristian Brauner fi 92*f93bc869SChristian Brauner} 93*f93bc869SChristian Brauner 94*f93bc869SChristian Braunertrap cleanup EXIT 95*f93bc869SChristian Brauner 96*f93bc869SChristian Braunerdie() { 97*f93bc869SChristian Brauner log_error "$*" 98*f93bc869SChristian Brauner exit 1 99*f93bc869SChristian Brauner} 100*f93bc869SChristian Brauner 101*f93bc869SChristian Braunerfind_dm_verity_keyring() { 102*f93bc869SChristian Brauner # The .dm-verity keyring is not linked to user-accessible keyrings, 103*f93bc869SChristian Brauner # so we need to find it via /proc/keys 104*f93bc869SChristian Brauner local serial_hex 105*f93bc869SChristian Brauner serial_hex=$(awk '/\.dm-verity/ {print $1}' /proc/keys 2>/dev/null) 106*f93bc869SChristian Brauner 107*f93bc869SChristian Brauner if [ -z "$serial_hex" ]; then 108*f93bc869SChristian Brauner return 1 109*f93bc869SChristian Brauner fi 110*f93bc869SChristian Brauner 111*f93bc869SChristian Brauner # Convert hex to decimal for keyctl 112*f93bc869SChristian Brauner echo $((16#$serial_hex)) 113*f93bc869SChristian Brauner} 114*f93bc869SChristian Brauner 115*f93bc869SChristian Braunerget_module_param() { 116*f93bc869SChristian Brauner local param="$1" 117*f93bc869SChristian Brauner local path="/sys/module/dm_verity/parameters/$param" 118*f93bc869SChristian Brauner 119*f93bc869SChristian Brauner if [ -f "$path" ]; then 120*f93bc869SChristian Brauner cat "$path" 121*f93bc869SChristian Brauner else 122*f93bc869SChristian Brauner echo "" 123*f93bc869SChristian Brauner fi 124*f93bc869SChristian Brauner} 125*f93bc869SChristian Brauner 126*f93bc869SChristian Braunercheck_requirements() { 127*f93bc869SChristian Brauner log_info "Checking requirements..." 128*f93bc869SChristian Brauner 129*f93bc869SChristian Brauner # Check for root 130*f93bc869SChristian Brauner if [ "$(id -u)" -ne 0 ]; then 131*f93bc869SChristian Brauner die "This script must be run as root" 132*f93bc869SChristian Brauner fi 133*f93bc869SChristian Brauner 134*f93bc869SChristian Brauner # Check for required tools 135*f93bc869SChristian Brauner for cmd in openssl veritysetup keyctl losetup dmsetup dd awk; do 136*f93bc869SChristian Brauner if ! command -v "$cmd" &>/dev/null; then 137*f93bc869SChristian Brauner die "Required command not found: $cmd" 138*f93bc869SChristian Brauner fi 139*f93bc869SChristian Brauner done 140*f93bc869SChristian Brauner 141*f93bc869SChristian Brauner # Check for dm-verity module 142*f93bc869SChristian Brauner if ! modprobe -n dm-verity &>/dev/null; then 143*f93bc869SChristian Brauner die "dm-verity module not available" 144*f93bc869SChristian Brauner fi 145*f93bc869SChristian Brauner 146*f93bc869SChristian Brauner # Verify OpenSSL can create signatures 147*f93bc869SChristian Brauner # OpenSSL cms -sign with -binary -outform DER creates detached signatures by default 148*f93bc869SChristian Brauner log_info "Using OpenSSL for PKCS#7 signatures" 149*f93bc869SChristian Brauner} 150*f93bc869SChristian Brauner 151*f93bc869SChristian Braunerload_dm_verity_module() { 152*f93bc869SChristian Brauner local keyring_unsealed="${1:-0}" 153*f93bc869SChristian Brauner local require_signatures="${2:-0}" 154*f93bc869SChristian Brauner 155*f93bc869SChristian Brauner log_info "Loading dm-verity module with keyring_unsealed=$keyring_unsealed require_signatures=$require_signatures" 156*f93bc869SChristian Brauner 157*f93bc869SChristian Brauner # Unload if already loaded 158*f93bc869SChristian Brauner if lsmod | grep -q '^dm_verity'; then 159*f93bc869SChristian Brauner log_info "Unloading existing dm-verity module..." 160*f93bc869SChristian Brauner modprobe -r dm-verity 2>/dev/null || \ 161*f93bc869SChristian Brauner die "Failed to unload dm-verity module (may be in use)" 162*f93bc869SChristian Brauner sleep 1 163*f93bc869SChristian Brauner fi 164*f93bc869SChristian Brauner 165*f93bc869SChristian Brauner # Load with specified parameters 166*f93bc869SChristian Brauner modprobe dm-verity keyring_unsealed="$keyring_unsealed" require_signatures="$require_signatures" || \ 167*f93bc869SChristian Brauner die "Failed to load dm-verity module" 168*f93bc869SChristian Brauner 169*f93bc869SChristian Brauner # Wait for keyring to be created (poll with timeout) 170*f93bc869SChristian Brauner local keyring_id="" 171*f93bc869SChristian Brauner local timeout=50 # 5 seconds (50 * 0.1s) 172*f93bc869SChristian Brauner while [ $timeout -gt 0 ]; do 173*f93bc869SChristian Brauner keyring_id=$(find_dm_verity_keyring) && break 174*f93bc869SChristian Brauner sleep 0.1 175*f93bc869SChristian Brauner timeout=$((timeout - 1)) 176*f93bc869SChristian Brauner done 177*f93bc869SChristian Brauner 178*f93bc869SChristian Brauner if [ -z "$keyring_id" ]; then 179*f93bc869SChristian Brauner die "dm-verity keyring not found after module load (timeout)" 180*f93bc869SChristian Brauner fi 181*f93bc869SChristian Brauner 182*f93bc869SChristian Brauner log_info "Found .dm-verity keyring: $keyring_id" 183*f93bc869SChristian Brauner echo "$keyring_id" > "$WORK_DIR/keyring_id" 184*f93bc869SChristian Brauner 185*f93bc869SChristian Brauner # Read and display module parameters 186*f93bc869SChristian Brauner KEYRING_UNSEALED=$(get_module_param "keyring_unsealed") 187*f93bc869SChristian Brauner REQUIRE_SIGNATURES=$(get_module_param "require_signatures") 188*f93bc869SChristian Brauner 189*f93bc869SChristian Brauner log_info "Module parameters:" 190*f93bc869SChristian Brauner log_info " keyring_unsealed=$KEYRING_UNSEALED" 191*f93bc869SChristian Brauner log_info " require_signatures=$REQUIRE_SIGNATURES" 192*f93bc869SChristian Brauner} 193*f93bc869SChristian Brauner 194*f93bc869SChristian Braunerunload_dm_verity_module() { 195*f93bc869SChristian Brauner log_info "Unloading dm-verity module..." 196*f93bc869SChristian Brauner 197*f93bc869SChristian Brauner # Clean up any dm-verity devices first 198*f93bc869SChristian Brauner local dm_dev 199*f93bc869SChristian Brauner while read -r dm_dev _; do 200*f93bc869SChristian Brauner [ -n "$dm_dev" ] || continue 201*f93bc869SChristian Brauner log_info "Removing dm-verity device: $dm_dev" 202*f93bc869SChristian Brauner dmsetup remove "$dm_dev" 2>/dev/null || true 203*f93bc869SChristian Brauner done < <(dmsetup ls --target verity 2>/dev/null) 204*f93bc869SChristian Brauner 205*f93bc869SChristian Brauner if lsmod | grep -q '^dm_verity'; then 206*f93bc869SChristian Brauner modprobe -r dm-verity 2>/dev/null || \ 207*f93bc869SChristian Brauner log_warn "Failed to unload dm-verity module" 208*f93bc869SChristian Brauner sleep 1 209*f93bc869SChristian Brauner fi 210*f93bc869SChristian Brauner} 211*f93bc869SChristian Brauner 212*f93bc869SChristian Braunergenerate_keys() { 213*f93bc869SChristian Brauner log_info "Generating signing key pair..." 214*f93bc869SChristian Brauner 215*f93bc869SChristian Brauner # Generate private key (2048-bit for faster test execution) 216*f93bc869SChristian Brauner openssl genrsa -out "$WORK_DIR/private.pem" 2048 2>/dev/null 217*f93bc869SChristian Brauner 218*f93bc869SChristian Brauner # Create OpenSSL config for certificate extensions 219*f93bc869SChristian Brauner # The kernel requires digitalSignature key usage for signature verification 220*f93bc869SChristian Brauner # Both subjectKeyIdentifier and authorityKeyIdentifier are needed for 221*f93bc869SChristian Brauner # the kernel to match keys in the keyring (especially for self-signed certs) 222*f93bc869SChristian Brauner cat > "$WORK_DIR/openssl.cnf" << 'EOF' 223*f93bc869SChristian Brauner[req] 224*f93bc869SChristian Braunerdistinguished_name = req_distinguished_name 225*f93bc869SChristian Braunerx509_extensions = v3_ca 226*f93bc869SChristian Braunerprompt = no 227*f93bc869SChristian Brauner 228*f93bc869SChristian Brauner[req_distinguished_name] 229*f93bc869SChristian BraunerCN = dm-verity-test-key 230*f93bc869SChristian Brauner 231*f93bc869SChristian Brauner[v3_ca] 232*f93bc869SChristian BraunerbasicConstraints = critical,CA:FALSE 233*f93bc869SChristian BraunerkeyUsage = digitalSignature 234*f93bc869SChristian BraunersubjectKeyIdentifier = hash 235*f93bc869SChristian BraunerauthorityKeyIdentifier = keyid 236*f93bc869SChristian BraunerEOF 237*f93bc869SChristian Brauner 238*f93bc869SChristian Brauner # Generate self-signed certificate with proper extensions 239*f93bc869SChristian Brauner openssl req -new -x509 -key "$WORK_DIR/private.pem" \ 240*f93bc869SChristian Brauner -out "$WORK_DIR/cert.pem" -days 365 \ 241*f93bc869SChristian Brauner -config "$WORK_DIR/openssl.cnf" 2>/dev/null 242*f93bc869SChristian Brauner 243*f93bc869SChristian Brauner # Convert certificate to DER format for kernel 244*f93bc869SChristian Brauner openssl x509 -in "$WORK_DIR/cert.pem" -outform DER \ 245*f93bc869SChristian Brauner -out "$WORK_DIR/cert.der" 246*f93bc869SChristian Brauner 247*f93bc869SChristian Brauner # Show certificate info for debugging 248*f93bc869SChristian Brauner log_info "Certificate details:" 249*f93bc869SChristian Brauner openssl x509 -in "$WORK_DIR/cert.pem" -noout -text 2>/dev/null | \ 250*f93bc869SChristian Brauner grep -E "Subject:|Issuer:|Key Usage|Extended" | head -10 251*f93bc869SChristian Brauner 252*f93bc869SChristian Brauner log_info "Keys generated successfully" 253*f93bc869SChristian Brauner} 254*f93bc869SChristian Brauner 255*f93bc869SChristian Braunerseal_keyring() { 256*f93bc869SChristian Brauner log_info "Sealing the .dm-verity keyring..." 257*f93bc869SChristian Brauner 258*f93bc869SChristian Brauner local keyring_id 259*f93bc869SChristian Brauner keyring_id=$(cat "$WORK_DIR/keyring_id") 260*f93bc869SChristian Brauner 261*f93bc869SChristian Brauner keyctl restrict_keyring "$keyring_id" || \ 262*f93bc869SChristian Brauner die "Failed to seal keyring" 263*f93bc869SChristian Brauner 264*f93bc869SChristian Brauner log_info "Keyring sealed successfully" 265*f93bc869SChristian Brauner} 266*f93bc869SChristian Brauner 267*f93bc869SChristian Braunercreate_test_device() { 268*f93bc869SChristian Brauner log_info "Creating test device images..." 269*f93bc869SChristian Brauner 270*f93bc869SChristian Brauner # Create data image with random content (8MB is sufficient for testing) 271*f93bc869SChristian Brauner dd if=/dev/urandom of="$WORK_DIR/data.img" bs=1M count=8 status=none 272*f93bc869SChristian Brauner 273*f93bc869SChristian Brauner # Create hash image (will be populated by veritysetup) 274*f93bc869SChristian Brauner dd if=/dev/zero of="$WORK_DIR/hash.img" bs=1M count=1 status=none 275*f93bc869SChristian Brauner 276*f93bc869SChristian Brauner # Setup loop devices 277*f93bc869SChristian Brauner DATA_DEV=$(losetup --find --show "$WORK_DIR/data.img") 278*f93bc869SChristian Brauner HASH_DEV=$(losetup --find --show "$WORK_DIR/hash.img") 279*f93bc869SChristian Brauner 280*f93bc869SChristian Brauner log_info "Data device: $DATA_DEV" 281*f93bc869SChristian Brauner log_info "Hash device: $HASH_DEV" 282*f93bc869SChristian Brauner} 283*f93bc869SChristian Brauner 284*f93bc869SChristian Braunercreate_verity_hash() { 285*f93bc869SChristian Brauner log_info "Creating dm-verity hash tree..." 286*f93bc869SChristian Brauner 287*f93bc869SChristian Brauner local root_hash output 288*f93bc869SChristian Brauner output=$(veritysetup format "$DATA_DEV" "$HASH_DEV" 2>&1) 289*f93bc869SChristian Brauner root_hash=$(echo "$output" | grep "Root hash:" | awk '{print $3}') 290*f93bc869SChristian Brauner 291*f93bc869SChristian Brauner if [ -z "$root_hash" ]; then 292*f93bc869SChristian Brauner log_error "veritysetup format output:" 293*f93bc869SChristian Brauner echo "$output" | sed 's/^/ /' 294*f93bc869SChristian Brauner die "Failed to get root hash from veritysetup format" 295*f93bc869SChristian Brauner fi 296*f93bc869SChristian Brauner 297*f93bc869SChristian Brauner echo "$root_hash" > "$WORK_DIR/root_hash" 298*f93bc869SChristian Brauner log_info "Root hash: $root_hash" 299*f93bc869SChristian Brauner} 300*f93bc869SChristian Brauner 301*f93bc869SChristian Braunercreate_detached_signature() { 302*f93bc869SChristian Brauner local infile="$1" 303*f93bc869SChristian Brauner local outfile="$2" 304*f93bc869SChristian Brauner local cert="$3" 305*f93bc869SChristian Brauner local key="$4" 306*f93bc869SChristian Brauner 307*f93bc869SChristian Brauner # Use openssl smime (not cms) for PKCS#7 signatures compatible with kernel 308*f93bc869SChristian Brauner # Flags from working veritysetup example: 309*f93bc869SChristian Brauner # -nocerts: don't include certificate in signature 310*f93bc869SChristian Brauner # -noattr: no signed attributes 311*f93bc869SChristian Brauner # -binary: binary input mode 312*f93bc869SChristian Brauner if openssl smime -sign -nocerts -noattr -binary \ 313*f93bc869SChristian Brauner -in "$infile" \ 314*f93bc869SChristian Brauner -inkey "$key" \ 315*f93bc869SChristian Brauner -signer "$cert" \ 316*f93bc869SChristian Brauner -outform der \ 317*f93bc869SChristian Brauner -out "$outfile" 2>/dev/null; then 318*f93bc869SChristian Brauner return 0 319*f93bc869SChristian Brauner fi 320*f93bc869SChristian Brauner 321*f93bc869SChristian Brauner log_error "Failed to create signature" 322*f93bc869SChristian Brauner return 1 323*f93bc869SChristian Brauner} 324*f93bc869SChristian Brauner 325*f93bc869SChristian Brauneractivate_verity_device() { 326*f93bc869SChristian Brauner local with_sig="$1" 327*f93bc869SChristian Brauner local root_hash 328*f93bc869SChristian Brauner root_hash=$(cat "$WORK_DIR/root_hash") 329*f93bc869SChristian Brauner 330*f93bc869SChristian Brauner # Clear dmesg and capture any kernel messages during activation 331*f93bc869SChristian Brauner dmesg -C 2>/dev/null || true 332*f93bc869SChristian Brauner 333*f93bc869SChristian Brauner if [ "$with_sig" = "yes" ]; then 334*f93bc869SChristian Brauner log_info "Activating dm-verity device with signature..." 335*f93bc869SChristian Brauner veritysetup open "$DATA_DEV" "$DM_NAME" "$HASH_DEV" "$root_hash" \ 336*f93bc869SChristian Brauner --root-hash-signature="$WORK_DIR/root_hash.p7s" 2>&1 337*f93bc869SChristian Brauner local ret=$? 338*f93bc869SChristian Brauner else 339*f93bc869SChristian Brauner log_info "Activating dm-verity device without signature..." 340*f93bc869SChristian Brauner veritysetup open "$DATA_DEV" "$DM_NAME" "$HASH_DEV" "$root_hash" 2>&1 341*f93bc869SChristian Brauner local ret=$? 342*f93bc869SChristian Brauner fi 343*f93bc869SChristian Brauner 344*f93bc869SChristian Brauner # Show relevant kernel messages 345*f93bc869SChristian Brauner local kmsg 346*f93bc869SChristian Brauner kmsg=$(dmesg 2>/dev/null | grep -i -E 'verity|pkcs|signature|asymmetric|key' | tail -10) 347*f93bc869SChristian Brauner if [ -n "$kmsg" ]; then 348*f93bc869SChristian Brauner log_info "Kernel messages:" 349*f93bc869SChristian Brauner echo "$kmsg" | while read -r line; do echo " $line"; done 350*f93bc869SChristian Brauner fi 351*f93bc869SChristian Brauner 352*f93bc869SChristian Brauner return $ret 353*f93bc869SChristian Brauner} 354*f93bc869SChristian Brauner 355*f93bc869SChristian Braunerdeactivate_verity_device() { 356*f93bc869SChristian Brauner if dmsetup info "$DM_NAME" &>/dev/null; then 357*f93bc869SChristian Brauner dmsetup remove "$DM_NAME" 2>/dev/null || true 358*f93bc869SChristian Brauner fi 359*f93bc869SChristian Brauner} 360*f93bc869SChristian Brauner 361*f93bc869SChristian Braunershow_keyring_status() { 362*f93bc869SChristian Brauner log_info "Keyring status:" 363*f93bc869SChristian Brauner 364*f93bc869SChristian Brauner local keyring_id 365*f93bc869SChristian Brauner keyring_id=$(find_dm_verity_keyring) || true 366*f93bc869SChristian Brauner 367*f93bc869SChristian Brauner if [ -n "$keyring_id" ]; then 368*f93bc869SChristian Brauner echo " Keyring ID: $keyring_id" 369*f93bc869SChristian Brauner keyctl show "$keyring_id" 2>/dev/null || true 370*f93bc869SChristian Brauner grep '\.dm-verity' /proc/keys 2>/dev/null || true 371*f93bc869SChristian Brauner fi 372*f93bc869SChristian Brauner} 373*f93bc869SChristian Brauner 374*f93bc869SChristian Braunerlist_keyring_keys() { 375*f93bc869SChristian Brauner log_info "Keys in .dm-verity keyring:" 376*f93bc869SChristian Brauner 377*f93bc869SChristian Brauner local keyring_id 378*f93bc869SChristian Brauner keyring_id=$(cat "$WORK_DIR/keyring_id" 2>/dev/null) || \ 379*f93bc869SChristian Brauner keyring_id=$(find_dm_verity_keyring) || true 380*f93bc869SChristian Brauner 381*f93bc869SChristian Brauner if [ -z "$keyring_id" ]; then 382*f93bc869SChristian Brauner log_warn "Could not find keyring" 383*f93bc869SChristian Brauner return 384*f93bc869SChristian Brauner fi 385*f93bc869SChristian Brauner 386*f93bc869SChristian Brauner # List all keys in the keyring 387*f93bc869SChristian Brauner local keys 388*f93bc869SChristian Brauner keys=$(keyctl list "$keyring_id" 2>/dev/null) 389*f93bc869SChristian Brauner if [ -z "$keys" ] || [ "$keys" = "keyring is empty" ]; then 390*f93bc869SChristian Brauner echo " (empty)" 391*f93bc869SChristian Brauner else 392*f93bc869SChristian Brauner echo "$keys" | while read -r line; do 393*f93bc869SChristian Brauner echo " $line" 394*f93bc869SChristian Brauner done 395*f93bc869SChristian Brauner 396*f93bc869SChristian Brauner # Show detailed info for each key 397*f93bc869SChristian Brauner log_info "Key details:" 398*f93bc869SChristian Brauner keyctl list "$keyring_id" 2>/dev/null | awk '{print $1}' | grep -E '^[0-9]+$' | while read -r key_id; do 399*f93bc869SChristian Brauner echo " Key $key_id:" 400*f93bc869SChristian Brauner keyctl describe "$key_id" 2>/dev/null | sed 's/^/ /' 401*f93bc869SChristian Brauner done 402*f93bc869SChristian Brauner fi 403*f93bc869SChristian Brauner} 404*f93bc869SChristian Brauner 405*f93bc869SChristian Braunergenerate_named_key() { 406*f93bc869SChristian Brauner local name="$1" 407*f93bc869SChristian Brauner local key_dir="$WORK_DIR/keys/$name" 408*f93bc869SChristian Brauner 409*f93bc869SChristian Brauner mkdir -p "$key_dir" 410*f93bc869SChristian Brauner 411*f93bc869SChristian Brauner # Log to stderr so it doesn't interfere with return value 412*f93bc869SChristian Brauner echo "[INFO] Generating key pair: $name" >&2 413*f93bc869SChristian Brauner 414*f93bc869SChristian Brauner # Generate private key 415*f93bc869SChristian Brauner openssl genrsa -out "$key_dir/private.pem" 2048 2>/dev/null 416*f93bc869SChristian Brauner 417*f93bc869SChristian Brauner # Create OpenSSL config for certificate extensions 418*f93bc869SChristian Brauner # Both subjectKeyIdentifier and authorityKeyIdentifier are needed for 419*f93bc869SChristian Brauner # the kernel to match keys in the keyring (especially for self-signed certs) 420*f93bc869SChristian Brauner cat > "$key_dir/openssl.cnf" << EOF 421*f93bc869SChristian Brauner[req] 422*f93bc869SChristian Braunerdistinguished_name = req_distinguished_name 423*f93bc869SChristian Braunerx509_extensions = v3_ca 424*f93bc869SChristian Braunerprompt = no 425*f93bc869SChristian Brauner 426*f93bc869SChristian Brauner[req_distinguished_name] 427*f93bc869SChristian BraunerCN = dm-verity-test-$name 428*f93bc869SChristian Brauner 429*f93bc869SChristian Brauner[v3_ca] 430*f93bc869SChristian BraunerbasicConstraints = critical,CA:FALSE 431*f93bc869SChristian BraunerkeyUsage = digitalSignature 432*f93bc869SChristian BraunersubjectKeyIdentifier = hash 433*f93bc869SChristian BraunerauthorityKeyIdentifier = keyid 434*f93bc869SChristian BraunerEOF 435*f93bc869SChristian Brauner 436*f93bc869SChristian Brauner # Generate self-signed certificate with proper extensions 437*f93bc869SChristian Brauner openssl req -new -x509 -key "$key_dir/private.pem" \ 438*f93bc869SChristian Brauner -out "$key_dir/cert.pem" -days 365 \ 439*f93bc869SChristian Brauner -config "$key_dir/openssl.cnf" 2>/dev/null 440*f93bc869SChristian Brauner 441*f93bc869SChristian Brauner # Convert certificate to DER format for kernel 442*f93bc869SChristian Brauner openssl x509 -in "$key_dir/cert.pem" -outform DER \ 443*f93bc869SChristian Brauner -out "$key_dir/cert.der" 444*f93bc869SChristian Brauner 445*f93bc869SChristian Brauner # Return the key directory path (only this goes to stdout) 446*f93bc869SChristian Brauner echo "$key_dir" 447*f93bc869SChristian Brauner} 448*f93bc869SChristian Brauner 449*f93bc869SChristian Braunerupload_named_key() { 450*f93bc869SChristian Brauner local name="$1" 451*f93bc869SChristian Brauner local key_dir="$2" 452*f93bc869SChristian Brauner 453*f93bc869SChristian Brauner local keyring_id 454*f93bc869SChristian Brauner keyring_id=$(cat "$WORK_DIR/keyring_id") 455*f93bc869SChristian Brauner 456*f93bc869SChristian Brauner log_info "Uploading key '$name' to keyring..." 457*f93bc869SChristian Brauner 458*f93bc869SChristian Brauner local key_id 459*f93bc869SChristian Brauner if key_id=$(keyctl padd asymmetric "$name" "$keyring_id" \ 460*f93bc869SChristian Brauner < "$key_dir/cert.der" 2>&1); then 461*f93bc869SChristian Brauner log_info "Key '$name' uploaded with ID: $key_id" 462*f93bc869SChristian Brauner echo "$key_id" > "$key_dir/key_id" 463*f93bc869SChristian Brauner return 0 464*f93bc869SChristian Brauner else 465*f93bc869SChristian Brauner log_error "Failed to upload key '$name': $key_id" 466*f93bc869SChristian Brauner return 1 467*f93bc869SChristian Brauner fi 468*f93bc869SChristian Brauner} 469*f93bc869SChristian Brauner 470*f93bc869SChristian Brauner# 471*f93bc869SChristian Brauner# Test: Verify sealed keyring rejects key additions 472*f93bc869SChristian Brauner# 473*f93bc869SChristian Braunertest_sealed_keyring_rejects_keys() { 474*f93bc869SChristian Brauner log_info "TEST: Verify sealed keyring rejects key additions" 475*f93bc869SChristian Brauner 476*f93bc869SChristian Brauner local keyring_id 477*f93bc869SChristian Brauner keyring_id=$(cat "$WORK_DIR/keyring_id") 478*f93bc869SChristian Brauner 479*f93bc869SChristian Brauner generate_keys 480*f93bc869SChristian Brauner 481*f93bc869SChristian Brauner # Try to add a key - should fail 482*f93bc869SChristian Brauner if keyctl padd asymmetric "dm-verity-test" "$keyring_id" \ 483*f93bc869SChristian Brauner < "$WORK_DIR/cert.der" 2>/dev/null; then 484*f93bc869SChristian Brauner log_fail "Key addition should have been rejected on sealed keyring" 485*f93bc869SChristian Brauner return 1 486*f93bc869SChristian Brauner else 487*f93bc869SChristian Brauner log_pass "Sealed keyring correctly rejected key addition" 488*f93bc869SChristian Brauner return 0 489*f93bc869SChristian Brauner fi 490*f93bc869SChristian Brauner} 491*f93bc869SChristian Brauner 492*f93bc869SChristian Brauner# 493*f93bc869SChristian Brauner# Test: Multiple keys in keyring 494*f93bc869SChristian Brauner# 495*f93bc869SChristian Braunertest_multiple_keys() { 496*f93bc869SChristian Brauner log_info "TEST: Multiple keys in keyring" 497*f93bc869SChristian Brauner 498*f93bc869SChristian Brauner local key1_dir key2_dir key3_dir 499*f93bc869SChristian Brauner 500*f93bc869SChristian Brauner # Generate three different keys 501*f93bc869SChristian Brauner key1_dir=$(generate_named_key "vendor-a") 502*f93bc869SChristian Brauner key2_dir=$(generate_named_key "vendor-b") 503*f93bc869SChristian Brauner key3_dir=$(generate_named_key "vendor-c") 504*f93bc869SChristian Brauner 505*f93bc869SChristian Brauner # Upload all three keys 506*f93bc869SChristian Brauner upload_named_key "vendor-a" "$key1_dir" || return 1 507*f93bc869SChristian Brauner upload_named_key "vendor-b" "$key2_dir" || return 1 508*f93bc869SChristian Brauner upload_named_key "vendor-c" "$key3_dir" || return 1 509*f93bc869SChristian Brauner 510*f93bc869SChristian Brauner log_info "" 511*f93bc869SChristian Brauner log_info "Keys in keyring before sealing:" 512*f93bc869SChristian Brauner list_keyring_keys 513*f93bc869SChristian Brauner show_keyring_status 514*f93bc869SChristian Brauner 515*f93bc869SChristian Brauner # Seal the keyring 516*f93bc869SChristian Brauner log_info "" 517*f93bc869SChristian Brauner seal_keyring 518*f93bc869SChristian Brauner 519*f93bc869SChristian Brauner # List keys after sealing 520*f93bc869SChristian Brauner log_info "" 521*f93bc869SChristian Brauner log_info "Keys in keyring after sealing:" 522*f93bc869SChristian Brauner list_keyring_keys 523*f93bc869SChristian Brauner show_keyring_status 524*f93bc869SChristian Brauner 525*f93bc869SChristian Brauner log_pass "Key upload and keyring sealing succeeded" 526*f93bc869SChristian Brauner 527*f93bc869SChristian Brauner # Create test device 528*f93bc869SChristian Brauner log_info "" 529*f93bc869SChristian Brauner create_test_device 530*f93bc869SChristian Brauner create_verity_hash 531*f93bc869SChristian Brauner 532*f93bc869SChristian Brauner # Test 1: Sign with key1, should verify successfully 533*f93bc869SChristian Brauner log_info "" 534*f93bc869SChristian Brauner log_info "Sub-test: Verify with vendor-a key" 535*f93bc869SChristian Brauner if ! sign_root_hash_with_key "$key1_dir"; then 536*f93bc869SChristian Brauner log_fail "Failed to sign with vendor-a key" 537*f93bc869SChristian Brauner return 1 538*f93bc869SChristian Brauner fi 539*f93bc869SChristian Brauner if activate_verity_device "yes"; then 540*f93bc869SChristian Brauner log_pass "Verification with vendor-a key succeeded" 541*f93bc869SChristian Brauner deactivate_verity_device 542*f93bc869SChristian Brauner else 543*f93bc869SChristian Brauner log_fail "Verification with vendor-a key should succeed" 544*f93bc869SChristian Brauner return 1 545*f93bc869SChristian Brauner fi 546*f93bc869SChristian Brauner 547*f93bc869SChristian Brauner # Test 2: Sign with key2, should also verify successfully 548*f93bc869SChristian Brauner log_info "" 549*f93bc869SChristian Brauner log_info "Sub-test: Verify with vendor-b key" 550*f93bc869SChristian Brauner if ! sign_root_hash_with_key "$key2_dir"; then 551*f93bc869SChristian Brauner log_fail "Failed to sign with vendor-b key" 552*f93bc869SChristian Brauner return 1 553*f93bc869SChristian Brauner fi 554*f93bc869SChristian Brauner if activate_verity_device "yes"; then 555*f93bc869SChristian Brauner log_pass "Verification with vendor-b key succeeded" 556*f93bc869SChristian Brauner deactivate_verity_device 557*f93bc869SChristian Brauner else 558*f93bc869SChristian Brauner log_fail "Verification with vendor-b key should succeed" 559*f93bc869SChristian Brauner return 1 560*f93bc869SChristian Brauner fi 561*f93bc869SChristian Brauner 562*f93bc869SChristian Brauner # Test 3: Sign with key3, should also verify successfully 563*f93bc869SChristian Brauner log_info "" 564*f93bc869SChristian Brauner log_info "Sub-test: Verify with vendor-c key" 565*f93bc869SChristian Brauner if ! sign_root_hash_with_key "$key3_dir"; then 566*f93bc869SChristian Brauner log_fail "Failed to sign with vendor-c key" 567*f93bc869SChristian Brauner return 1 568*f93bc869SChristian Brauner fi 569*f93bc869SChristian Brauner if activate_verity_device "yes"; then 570*f93bc869SChristian Brauner log_pass "Verification with vendor-c key succeeded" 571*f93bc869SChristian Brauner deactivate_verity_device 572*f93bc869SChristian Brauner else 573*f93bc869SChristian Brauner log_fail "Verification with vendor-c key should succeed" 574*f93bc869SChristian Brauner return 1 575*f93bc869SChristian Brauner fi 576*f93bc869SChristian Brauner 577*f93bc869SChristian Brauner # Test 4: Generate a key NOT in the keyring, should fail 578*f93bc869SChristian Brauner log_info "" 579*f93bc869SChristian Brauner log_info "Sub-test: Verify with unknown key (should fail)" 580*f93bc869SChristian Brauner local unknown_key_dir 581*f93bc869SChristian Brauner unknown_key_dir=$(generate_named_key "unknown-vendor") 582*f93bc869SChristian Brauner if ! sign_root_hash_with_key "$unknown_key_dir"; then 583*f93bc869SChristian Brauner log_fail "Failed to sign with unknown-vendor key" 584*f93bc869SChristian Brauner return 1 585*f93bc869SChristian Brauner fi 586*f93bc869SChristian Brauner if activate_verity_device "yes"; then 587*f93bc869SChristian Brauner log_fail "Verification with unknown key should fail" 588*f93bc869SChristian Brauner deactivate_verity_device 589*f93bc869SChristian Brauner return 1 590*f93bc869SChristian Brauner else 591*f93bc869SChristian Brauner log_pass "Verification with unknown key correctly rejected" 592*f93bc869SChristian Brauner fi 593*f93bc869SChristian Brauner 594*f93bc869SChristian Brauner log_info "" 595*f93bc869SChristian Brauner log_pass "Multiple keys test completed successfully" 596*f93bc869SChristian Brauner return 0 597*f93bc869SChristian Brauner} 598*f93bc869SChristian Brauner 599*f93bc869SChristian Braunersign_root_hash_with_key() { 600*f93bc869SChristian Brauner local key_dir="$1" 601*f93bc869SChristian Brauner 602*f93bc869SChristian Brauner local root_hash 603*f93bc869SChristian Brauner root_hash=$(cat "$WORK_DIR/root_hash") 604*f93bc869SChristian Brauner 605*f93bc869SChristian Brauner # Create the data to sign (hex string, not binary) 606*f93bc869SChristian Brauner echo -n "$root_hash" > "$WORK_DIR/root_hash.txt" 607*f93bc869SChristian Brauner 608*f93bc869SChristian Brauner # Debug: show exactly what we're signing 609*f93bc869SChristian Brauner log_info "Root hash (hex): $root_hash" 610*f93bc869SChristian Brauner log_info "Root hash hex string size: $(wc -c < "$WORK_DIR/root_hash.txt") bytes" 611*f93bc869SChristian Brauner 612*f93bc869SChristian Brauner # Create detached PKCS#7 signature 613*f93bc869SChristian Brauner if ! create_detached_signature "$WORK_DIR/root_hash.txt" "$WORK_DIR/root_hash.p7s" \ 614*f93bc869SChristian Brauner "$key_dir/cert.pem" "$key_dir/private.pem"; then 615*f93bc869SChristian Brauner log_error "Failed to sign root hash with key from $key_dir" 616*f93bc869SChristian Brauner return 1 617*f93bc869SChristian Brauner fi 618*f93bc869SChristian Brauner 619*f93bc869SChristian Brauner # Debug: show signing certificate info 620*f93bc869SChristian Brauner log_info "Signed with certificate:" 621*f93bc869SChristian Brauner openssl x509 -in "$key_dir/cert.pem" -noout -subject 2>/dev/null | sed 's/^/ /' 622*f93bc869SChristian Brauner 623*f93bc869SChristian Brauner # Debug: verify signature locally 624*f93bc869SChristian Brauner # -nointern: cert not in signature, use -certfile 625*f93bc869SChristian Brauner # -noverify: skip certificate chain validation (self-signed) 626*f93bc869SChristian Brauner if openssl smime -verify -binary -inform der -nointern -noverify \ 627*f93bc869SChristian Brauner -in "$WORK_DIR/root_hash.p7s" \ 628*f93bc869SChristian Brauner -content "$WORK_DIR/root_hash.txt" \ 629*f93bc869SChristian Brauner -certfile "$key_dir/cert.pem" \ 630*f93bc869SChristian Brauner -out /dev/null 2>/dev/null; then 631*f93bc869SChristian Brauner log_info "Local signature verification: PASSED" 632*f93bc869SChristian Brauner else 633*f93bc869SChristian Brauner log_warn "Local signature verification: FAILED" 634*f93bc869SChristian Brauner fi 635*f93bc869SChristian Brauner return 0 636*f93bc869SChristian Brauner} 637*f93bc869SChristian Brauner 638*f93bc869SChristian Brauner# 639*f93bc869SChristian Brauner# Test: Verify corrupted signatures are rejected 640*f93bc869SChristian Brauner# 641*f93bc869SChristian Braunertest_corrupted_signature() { 642*f93bc869SChristian Brauner log_info "TEST: Verify corrupted signatures are rejected" 643*f93bc869SChristian Brauner 644*f93bc869SChristian Brauner # This test requires a valid setup from test_multiple_keys or similar 645*f93bc869SChristian Brauner # It modifies the signature file and verifies rejection 646*f93bc869SChristian Brauner 647*f93bc869SChristian Brauner if [ ! -f "$WORK_DIR/root_hash.p7s" ]; then 648*f93bc869SChristian Brauner log_warn "No signature file found, skipping corrupted signature test" 649*f93bc869SChristian Brauner return 0 650*f93bc869SChristian Brauner fi 651*f93bc869SChristian Brauner 652*f93bc869SChristian Brauner # Save original signature 653*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s" "$WORK_DIR/root_hash.p7s.orig" 654*f93bc869SChristian Brauner 655*f93bc869SChristian Brauner # Test 1: Truncated signature 656*f93bc869SChristian Brauner log_info "Sub-test: Truncated signature (should fail)" 657*f93bc869SChristian Brauner head -c 100 "$WORK_DIR/root_hash.p7s.orig" > "$WORK_DIR/root_hash.p7s" 658*f93bc869SChristian Brauner if activate_verity_device "yes"; then 659*f93bc869SChristian Brauner log_fail "Truncated signature should be rejected" 660*f93bc869SChristian Brauner deactivate_verity_device 661*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s.orig" "$WORK_DIR/root_hash.p7s" 662*f93bc869SChristian Brauner return 1 663*f93bc869SChristian Brauner else 664*f93bc869SChristian Brauner log_pass "Truncated signature correctly rejected" 665*f93bc869SChristian Brauner fi 666*f93bc869SChristian Brauner 667*f93bc869SChristian Brauner # Test 2: Corrupted signature (flip some bytes) 668*f93bc869SChristian Brauner log_info "Sub-test: Corrupted signature bytes (should fail)" 669*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s.orig" "$WORK_DIR/root_hash.p7s" 670*f93bc869SChristian Brauner # Corrupt bytes in the middle of the signature 671*f93bc869SChristian Brauner local sig_size 672*f93bc869SChristian Brauner sig_size=$(wc -c < "$WORK_DIR/root_hash.p7s") 673*f93bc869SChristian Brauner local corrupt_offset=$((sig_size / 2)) 674*f93bc869SChristian Brauner printf '\xff\xff\xff\xff' | dd of="$WORK_DIR/root_hash.p7s" bs=1 seek=$corrupt_offset conv=notrunc 2>/dev/null 675*f93bc869SChristian Brauner if activate_verity_device "yes"; then 676*f93bc869SChristian Brauner log_fail "Corrupted signature should be rejected" 677*f93bc869SChristian Brauner deactivate_verity_device 678*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s.orig" "$WORK_DIR/root_hash.p7s" 679*f93bc869SChristian Brauner return 1 680*f93bc869SChristian Brauner else 681*f93bc869SChristian Brauner log_pass "Corrupted signature correctly rejected" 682*f93bc869SChristian Brauner fi 683*f93bc869SChristian Brauner 684*f93bc869SChristian Brauner # Test 3: Signature over wrong data (sign different content) 685*f93bc869SChristian Brauner log_info "Sub-test: Signature over wrong data (should fail)" 686*f93bc869SChristian Brauner # Create a different root hash (all zeros as hex string) 687*f93bc869SChristian Brauner printf '%064d' 0 > "$WORK_DIR/wrong_hash.txt" 688*f93bc869SChristian Brauner # Get the first key directory that was used 689*f93bc869SChristian Brauner local key_dir="$WORK_DIR/keys/vendor-a" 690*f93bc869SChristian Brauner if [ -d "$key_dir" ]; then 691*f93bc869SChristian Brauner create_detached_signature "$WORK_DIR/wrong_hash.txt" "$WORK_DIR/root_hash.p7s" \ 692*f93bc869SChristian Brauner "$key_dir/cert.pem" "$key_dir/private.pem" 693*f93bc869SChristian Brauner if activate_verity_device "yes"; then 694*f93bc869SChristian Brauner log_fail "Signature over wrong data should be rejected" 695*f93bc869SChristian Brauner deactivate_verity_device 696*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s.orig" "$WORK_DIR/root_hash.p7s" 697*f93bc869SChristian Brauner return 1 698*f93bc869SChristian Brauner else 699*f93bc869SChristian Brauner log_pass "Signature over wrong data correctly rejected" 700*f93bc869SChristian Brauner fi 701*f93bc869SChristian Brauner else 702*f93bc869SChristian Brauner log_warn "Key directory not found, skipping wrong data test" 703*f93bc869SChristian Brauner fi 704*f93bc869SChristian Brauner 705*f93bc869SChristian Brauner # Restore original signature 706*f93bc869SChristian Brauner cp "$WORK_DIR/root_hash.p7s.orig" "$WORK_DIR/root_hash.p7s" 707*f93bc869SChristian Brauner 708*f93bc869SChristian Brauner log_pass "Corrupted signature test completed successfully" 709*f93bc869SChristian Brauner return 0 710*f93bc869SChristian Brauner} 711*f93bc869SChristian Brauner 712*f93bc869SChristian Brauner# 713*f93bc869SChristian Brauner# Test: Verify keyring is sealed when keyring_unsealed=0 714*f93bc869SChristian Brauner# 715*f93bc869SChristian Braunertest_keyring_sealed_by_default() { 716*f93bc869SChristian Brauner log_info "TEST: Verify keyring is sealed by default (keyring_unsealed=0)" 717*f93bc869SChristian Brauner 718*f93bc869SChristian Brauner local keyring_id 719*f93bc869SChristian Brauner keyring_id=$(cat "$WORK_DIR/keyring_id") 720*f93bc869SChristian Brauner 721*f93bc869SChristian Brauner log_info "Current keyring state (should be empty and sealed):" 722*f93bc869SChristian Brauner list_keyring_keys 723*f93bc869SChristian Brauner show_keyring_status 724*f93bc869SChristian Brauner 725*f93bc869SChristian Brauner generate_keys 726*f93bc869SChristian Brauner 727*f93bc869SChristian Brauner # Try to add a key - should fail if keyring is sealed 728*f93bc869SChristian Brauner log_info "Attempting to add key to sealed keyring..." 729*f93bc869SChristian Brauner if keyctl padd asymmetric "dm-verity-test" "$keyring_id" \ 730*f93bc869SChristian Brauner < "$WORK_DIR/cert.der" 2>/dev/null; then 731*f93bc869SChristian Brauner log_fail "Keyring should be sealed when keyring_unsealed=0" 732*f93bc869SChristian Brauner list_keyring_keys 733*f93bc869SChristian Brauner return 1 734*f93bc869SChristian Brauner else 735*f93bc869SChristian Brauner log_pass "Keyring is correctly sealed when keyring_unsealed=0" 736*f93bc869SChristian Brauner log_info "Keyring state after failed add attempt:" 737*f93bc869SChristian Brauner list_keyring_keys 738*f93bc869SChristian Brauner return 0 739*f93bc869SChristian Brauner fi 740*f93bc869SChristian Brauner} 741*f93bc869SChristian Brauner 742*f93bc869SChristian Brauner# 743*f93bc869SChristian Brauner# Test: Verify dm-verity keyring is inactive when sealed empty 744*f93bc869SChristian Brauner# 745*f93bc869SChristian Braunertest_keyring_inactive_when_empty() { 746*f93bc869SChristian Brauner log_info "TEST: Verify dm-verity keyring is inactive when sealed empty" 747*f93bc869SChristian Brauner 748*f93bc869SChristian Brauner # When keyring_unsealed=0, the keyring is sealed immediately while empty 749*f93bc869SChristian Brauner # This means it should NOT be used for verification (nr_leaves_on_tree=0) 750*f93bc869SChristian Brauner 751*f93bc869SChristian Brauner log_info "Keyring state (should be empty and sealed):" 752*f93bc869SChristian Brauner list_keyring_keys 753*f93bc869SChristian Brauner show_keyring_status 754*f93bc869SChristian Brauner 755*f93bc869SChristian Brauner create_test_device 756*f93bc869SChristian Brauner create_verity_hash 757*f93bc869SChristian Brauner 758*f93bc869SChristian Brauner # Without any keys in the dm-verity keyring, and with it sealed, 759*f93bc869SChristian Brauner # verification should fall through to the secondary/platform keyrings 760*f93bc869SChristian Brauner # and likely succeed (if require_signatures=0) or fail (if =1) 761*f93bc869SChristian Brauner 762*f93bc869SChristian Brauner log_info "Sub-test: Device activation with sealed empty keyring" 763*f93bc869SChristian Brauner if [ "$REQUIRE_SIGNATURES" = "Y" ] || [ "$REQUIRE_SIGNATURES" = "1" ]; then 764*f93bc869SChristian Brauner if activate_verity_device "no"; then 765*f93bc869SChristian Brauner log_fail "Device should NOT activate without signature when require_signatures=1" 766*f93bc869SChristian Brauner deactivate_verity_device 767*f93bc869SChristian Brauner return 1 768*f93bc869SChristian Brauner else 769*f93bc869SChristian Brauner log_pass "Device correctly rejected (require_signatures=1, no valid signature)" 770*f93bc869SChristian Brauner fi 771*f93bc869SChristian Brauner else 772*f93bc869SChristian Brauner if activate_verity_device "no"; then 773*f93bc869SChristian Brauner log_pass "Device activated (require_signatures=0, empty dm-verity keyring is inactive)" 774*f93bc869SChristian Brauner deactivate_verity_device 775*f93bc869SChristian Brauner else 776*f93bc869SChristian Brauner log_fail "Device should activate when require_signatures=0" 777*f93bc869SChristian Brauner return 1 778*f93bc869SChristian Brauner fi 779*f93bc869SChristian Brauner fi 780*f93bc869SChristian Brauner 781*f93bc869SChristian Brauner return 0 782*f93bc869SChristian Brauner} 783*f93bc869SChristian Brauner 784*f93bc869SChristian Braunermain() { 785*f93bc869SChristian Brauner local rc=0 786*f93bc869SChristian Brauner 787*f93bc869SChristian Brauner log_info "=== dm-verity keyring test ===" 788*f93bc869SChristian Brauner log_info "" 789*f93bc869SChristian Brauner 790*f93bc869SChristian Brauner # Create work directory 791*f93bc869SChristian Brauner WORK_DIR=$(mktemp -d -t dm-verity-test.XXXXXX) 792*f93bc869SChristian Brauner log_info "Work directory: $WORK_DIR" 793*f93bc869SChristian Brauner 794*f93bc869SChristian Brauner check_requirements 795*f93bc869SChristian Brauner 796*f93bc869SChristian Brauner # 797*f93bc869SChristian Brauner # Test 1: UNSEALED keyring mode (keyring_unsealed=1) 798*f93bc869SChristian Brauner # 799*f93bc869SChristian Brauner log_info "" 800*f93bc869SChristian Brauner log_info "========================================" 801*f93bc869SChristian Brauner log_info "=== TEST MODE: UNSEALED KEYRING ===" 802*f93bc869SChristian Brauner log_info "========================================" 803*f93bc869SChristian Brauner log_info "" 804*f93bc869SChristian Brauner 805*f93bc869SChristian Brauner load_dm_verity_module 1 1 # keyring_unsealed=1, require_signatures=1 806*f93bc869SChristian Brauner show_keyring_status 807*f93bc869SChristian Brauner 808*f93bc869SChristian Brauner log_info "" 809*f93bc869SChristian Brauner if ! test_multiple_keys; then 810*f93bc869SChristian Brauner rc=1 811*f93bc869SChristian Brauner fi 812*f93bc869SChristian Brauner 813*f93bc869SChristian Brauner # After sealing, verify it rejects new keys 814*f93bc869SChristian Brauner log_info "" 815*f93bc869SChristian Brauner if ! test_sealed_keyring_rejects_keys; then 816*f93bc869SChristian Brauner rc=1 817*f93bc869SChristian Brauner fi 818*f93bc869SChristian Brauner 819*f93bc869SChristian Brauner # Test corrupted signatures are rejected 820*f93bc869SChristian Brauner log_info "" 821*f93bc869SChristian Brauner if ! test_corrupted_signature; then 822*f93bc869SChristian Brauner rc=1 823*f93bc869SChristian Brauner fi 824*f93bc869SChristian Brauner 825*f93bc869SChristian Brauner # Clean up devices before reloading module 826*f93bc869SChristian Brauner deactivate_verity_device 827*f93bc869SChristian Brauner if [ -n "$DATA_DEV" ] && [[ "$DATA_DEV" == /dev/loop* ]]; then 828*f93bc869SChristian Brauner losetup -d "$DATA_DEV" 2>/dev/null || true 829*f93bc869SChristian Brauner DATA_DEV="" 830*f93bc869SChristian Brauner fi 831*f93bc869SChristian Brauner if [ -n "$HASH_DEV" ] && [[ "$HASH_DEV" == /dev/loop* ]]; then 832*f93bc869SChristian Brauner losetup -d "$HASH_DEV" 2>/dev/null || true 833*f93bc869SChristian Brauner HASH_DEV="" 834*f93bc869SChristian Brauner fi 835*f93bc869SChristian Brauner 836*f93bc869SChristian Brauner # 837*f93bc869SChristian Brauner # Test 2: SEALED keyring mode (keyring_unsealed=0, default) 838*f93bc869SChristian Brauner # 839*f93bc869SChristian Brauner log_info "" 840*f93bc869SChristian Brauner log_info "========================================" 841*f93bc869SChristian Brauner log_info "=== TEST MODE: SEALED KEYRING (default) ===" 842*f93bc869SChristian Brauner log_info "========================================" 843*f93bc869SChristian Brauner log_info "" 844*f93bc869SChristian Brauner 845*f93bc869SChristian Brauner load_dm_verity_module 0 0 # keyring_unsealed=0, require_signatures=0 846*f93bc869SChristian Brauner show_keyring_status 847*f93bc869SChristian Brauner 848*f93bc869SChristian Brauner log_info "" 849*f93bc869SChristian Brauner if ! test_keyring_sealed_by_default; then 850*f93bc869SChristian Brauner rc=1 851*f93bc869SChristian Brauner fi 852*f93bc869SChristian Brauner 853*f93bc869SChristian Brauner log_info "" 854*f93bc869SChristian Brauner if ! test_keyring_inactive_when_empty; then 855*f93bc869SChristian Brauner rc=1 856*f93bc869SChristian Brauner fi 857*f93bc869SChristian Brauner 858*f93bc869SChristian Brauner # 859*f93bc869SChristian Brauner # Summary 860*f93bc869SChristian Brauner # 861*f93bc869SChristian Brauner log_info "" 862*f93bc869SChristian Brauner log_info "========================================" 863*f93bc869SChristian Brauner if [ $rc -eq 0 ]; then 864*f93bc869SChristian Brauner log_info "=== All tests PASSED ===" 865*f93bc869SChristian Brauner else 866*f93bc869SChristian Brauner log_error "=== Some tests FAILED ===" 867*f93bc869SChristian Brauner fi 868*f93bc869SChristian Brauner log_info "========================================" 869*f93bc869SChristian Brauner 870*f93bc869SChristian Brauner return $rc 871*f93bc869SChristian Brauner} 872*f93bc869SChristian Brauner 873*f93bc869SChristian Braunermain "$@" 874