Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests: add dm-verity keyring selftests

Add selftests that verify the keyring behaves correctly.
For simplicity this works with dm-verity as a module.

Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>

authored by

Christian Brauner and committed by
Mikulas Patocka
f93bc869 033724b1

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