|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +error_exit() { |
| 4 | + _error_msg="$1" |
| 5 | + echo "$_error_msg" |
| 6 | + exit 1 |
| 7 | +} |
| 8 | + |
| 9 | +error_check() { |
| 10 | + _error_code=$? |
| 11 | + _error_msg="$1" |
| 12 | + [ "$_error_code" -ne 0 ] && error_exit "$_error_msg : ($_error_code)" |
| 13 | + return 0 |
| 14 | +} |
| 15 | + |
| 16 | +check_using_pkg_mng() { |
| 17 | + # Get the distribution name |
| 18 | + distribution_name=$(lsb_release -is 2>/dev/null || cat /etc/*-release | grep '^ID=' | awk -F= '{print $2}' | tr -d '"') |
| 19 | + error_check "Cannot read information about distribution" |
| 20 | + |
| 21 | + # Initialize the variable to an empty string |
| 22 | + PKG_MNG="" |
| 23 | + |
| 24 | + # Map distribution names to package managers |
| 25 | + case "$distribution_name" in |
| 26 | + debian|ubuntu) |
| 27 | + PKG_MNG="apt" |
| 28 | + ;; |
| 29 | + redhat|centos|fedora) |
| 30 | + PKG_MNG="dnf" |
| 31 | + ;; |
| 32 | + arch) |
| 33 | + PKG_MNG="pacman" |
| 34 | + ;; |
| 35 | + openbsd) |
| 36 | + PKG_MNG="pkg" |
| 37 | + ;; |
| 38 | + gentoo) |
| 39 | + PKG_MNG="emerge" |
| 40 | + ;; |
| 41 | + *) |
| 42 | + PKG_MNG="Unknown" |
| 43 | + ;; |
| 44 | + esac |
| 45 | + |
| 46 | + DISTRO=$distribution_name |
| 47 | + # Display the detected package manager or "Unknown" if none is detected |
| 48 | + if [ "$PKG_MNG" != "Unknown" ]; then |
| 49 | + echo "Detected Package Manager: $PKG_MNG" |
| 50 | + echo "Detected Distro: $DISTRO" |
| 51 | + else |
| 52 | + echo "Unknown Package Manager" |
| 53 | + exit 1 |
| 54 | + fi |
| 55 | +} |
| 56 | + |
| 57 | +install_deps() { |
| 58 | + local deps=(sbsigntools faketime) |
| 59 | + local deps_test=(sbsign faketime) |
| 60 | + local all_installed="y" |
| 61 | + |
| 62 | + echo -n "checking dependencies: " |
| 63 | + for dependency in "${deps_test[@]}"; do |
| 64 | + echo -n "$dependency" |
| 65 | + if ! command -v "$dependency" >/dev/null; then |
| 66 | + echo -n "-" |
| 67 | + all_installed="n" |
| 68 | + else |
| 69 | + echo -n "+" |
| 70 | + fi |
| 71 | + echo -n ", " |
| 72 | + done |
| 73 | + echo "" |
| 74 | + |
| 75 | + if [ $all_installed = "y" ]; then |
| 76 | + return |
| 77 | + fi |
| 78 | + |
| 79 | + echo "Installing dependencies" |
| 80 | + check_using_pkg_mng |
| 81 | + |
| 82 | + if [ $DISTRO == "arch" ]; then |
| 83 | + sudo $PKG_MNG -S "${deps[@]}" 2>&1 |
| 84 | + error_check "Cannot install ${deps[*]}" |
| 85 | + else |
| 86 | + sudo $PKG_MNG install "${deps[@]}" 2>&1 |
| 87 | + error_check "Cannot install ${deps[*]}" |
| 88 | + fi |
| 89 | +} |
| 90 | + |
| 91 | +create_rsa_key() { |
| 92 | + algo=$1 |
| 93 | + openssl req -new -x509 -newkey rsa:$algo -subj "/CN=3mdeb_test/" -keyout cert.key -out cert.crt -days 3650 -nodes -sha256 > /dev/null 2>&1 |
| 94 | + error_check "Cannot create rsa key pair" |
| 95 | + openssl x509 -in cert.crt -outform der -out cert.der > /dev/null 2>&1 |
| 96 | + error_check "Cannot create rsa der cert" |
| 97 | +} |
| 98 | + |
| 99 | +create_ecdsa_key() { |
| 100 | + algo=$1 |
| 101 | + openssl req -new -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-$algo -subj "/CN=3mdeb_key/" -keyout cert.key -out cert.crt -days 3650 -nodes -sha256 > /dev/null 2>&1 |
| 102 | + error_check "Cannot create ecdsa key pair" |
| 103 | + openssl base64 -d -in cert.crt -out cert.der > /dev/null 2>&1 |
| 104 | + error_check "Cannot create ecdsa der cert" |
| 105 | +} |
| 106 | + |
| 107 | +create_intermediate_key() { |
| 108 | + # generate two key pairs, make CSR |
| 109 | + openssl req -new -x509 -newkey rsa:2048 -subj "/CN=3mdeb_test/" -keyout cert.key -out cert.crt -days 3650 -nodes -sha256 > /dev/null 2>&1 |
| 110 | + error_check "Cannot create rsa first key for intermediate" |
| 111 | + openssl req -new -x509 -newkey rsa:2048 -subj "/CN=3mdeb_test/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256 > /dev/null 2>&1 |
| 112 | + error_check "Cannot create rsa first key for intermediate" |
| 113 | + openssl req -new -key cert.key -out cert.csr -subj "/C=PL/O=3mdeb" > /dev/null 2>&1 |
| 114 | + error_check "Cannot create csr" |
| 115 | + # sign the CSR with the second key pair |
| 116 | + # its necessary to `touch cert.ext`, otherwise it says its not there |
| 117 | + touch cert.ext |
| 118 | + openssl x509 -req -in cert.csr -CA PK.crt -CAkey PK.key -out cert.crt -CAcreateserial -extfile cert.ext > /dev/null 2>&1 |
| 119 | + error_check "Cannot sign csr" |
| 120 | + openssl x509 -in cert.crt -outform der -out cert.der > /dev/null 2>&1 |
| 121 | + error_check "Cannot create der from signed cert" |
| 122 | +} |
| 123 | + |
| 124 | +create_bad_format_key() { |
| 125 | + algo=$1 |
| 126 | + openssl req -new -x509 -newkey rsa:$algo -subj "/CN=3mdeb_test/" -keyout cert.key -out cert.crt -days 3650 -nodes -sha256 > /dev/null 2>&1 |
| 127 | + error_check "Cannot create rsa key pair" |
| 128 | + # here we copy .crt to .der to leave wrong format for BIOS menu |
| 129 | + cp cert.crt cert.der |
| 130 | + error_check "Cannot create fake cert.der" |
| 131 | +} |
| 132 | + |
| 133 | +create_expired_cert() { |
| 134 | + faketime '10 days ago' openssl req -new -x509 -newkey rsa:2048 -subj "/CN=ExpiredCert/" -keyout cert.key -out cert.crt -days 10 -nodes -sha256 > /dev/null 2>&1 |
| 135 | + error_check "Cannot create expired rsa key pair" |
| 136 | + openssl x509 -in cert.crt -outform der -out cert.der |
| 137 | + error_check "Cannot create expired rsa der cert" |
| 138 | +} |
| 139 | + |
| 140 | +# Create FAT image containing everything in $FILES and create |
| 141 | +# `add-boot-options.sh` script |
| 142 | +create_iso() { |
| 143 | + IMAGELABEL="tests" |
| 144 | + IMAGEPATH="$TEMPDIR/$IMAGELABEL.img" |
| 145 | + local mounted= |
| 146 | + local dev= |
| 147 | + mounted="/run/media/$(whoami)/$IMAGELABEL" |
| 148 | + |
| 149 | + dd if=/dev/zero of="$IMAGEPATH" bs=1M count=10 > /dev/null 2>&1 |
| 150 | + error_check "Cannot create empty image file to store created certs and efi files" |
| 151 | + # Create GPT table and EFI partition |
| 152 | + fdisk "$IMAGEPATH" << EOF |
| 153 | +g |
| 154 | +n |
| 155 | +1 |
| 156 | +
|
| 157 | +
|
| 158 | +t |
| 159 | +1 |
| 160 | +w |
| 161 | +EOF |
| 162 | + error_check "fdisk failed" |
| 163 | + |
| 164 | + dev=$(udisksctl loop-setup -f "$IMAGEPATH" | awk '{print substr($NF,1,length($NF)-1)}') |
| 165 | + error_check "udisksctl failed to create loop device" |
| 166 | + trap "udisksctl loop-delete -b $dev; cleanup" EXIT |
| 167 | + echo "Creating fat partition on ${dev}p1" |
| 168 | + sudo mkfs.fat -F 12 "${dev}p1" -n $IMAGELABEL > /dev/null 2>&1 |
| 169 | + error_check "Cannot create fat partition" |
| 170 | + mounted=$(udisksctl mount -b "${dev}p1" | awk '{print $NF}') |
| 171 | + error_check "Cannot mount ${dev}p1" |
| 172 | + trap "umount $mounted; udisksctl loop-delete -b $dev; cleanup" EXIT |
| 173 | + |
| 174 | + # create script that'll add all efi files to boot options |
| 175 | + cat << EOF >> "$FILES/add-boot-options.sh" |
| 176 | +#!/bin/bash |
| 177 | +
|
| 178 | +UUID=$(lsblk -no UUID "${dev}p1") |
| 179 | +PARTITION=\$(basename "\$(realpath "/dev/disk/by-uuid/\$UUID")") |
| 180 | +PARTITION_DEV=/dev/\$PARTITION |
| 181 | +DEV="/dev/\$(basename "\$(realpath /sys/class/block/\$PARTITION/..)")" |
| 182 | +TEMP_FILE=\$(mktemp -d) |
| 183 | +
|
| 184 | +mount \$PARTITION_DEV \$TEMP_FILE |
| 185 | +cd \$TEMP_FILE |
| 186 | +find SBO0* -name "*.efi" -exec \\ |
| 187 | + efibootmgr --create --disk=\$DEV --label="{}" \\ |
| 188 | + --loader="\$(echo "{}" | sed 's|/|\\\\|g')" \; |
| 189 | +EOF |
| 190 | + |
| 191 | + cp -r "$FILES"/* "$mounted" |
| 192 | + error_check "Couldn't copy $FILES/ to $mounted" |
| 193 | + |
| 194 | + trap 'cleanup' EXIT |
| 195 | + if ! umount "$mounted"; then |
| 196 | + udisksctl loop-delete -b "$dev" |
| 197 | + error_exit "Failed to umount $mounted" |
| 198 | + fi |
| 199 | + udisksctl loop-delete -b "$dev" |
| 200 | + error_check "Couldn't delete loop device $dev" |
| 201 | +} |
| 202 | + |
| 203 | +# create_test <test_name> <sign_efi> <create_key_function> [args...] |
| 204 | +create_test() { |
| 205 | + local TEST=$1 |
| 206 | + local SIGN="$2" |
| 207 | + shift 2 |
| 208 | + mkdir "$TEST" |
| 209 | + |
| 210 | + pushd "$TEST" >/dev/null || error_exit "Couldn't enter $TEST" |
| 211 | + |
| 212 | + # call <create_key_function> [args...] |
| 213 | + "$@" |
| 214 | + error_check "$*: Couldn't create keys and certificate" |
| 215 | + if [ "$SIGN" = "y" ]; then |
| 216 | + sbsign --key cert.key --cert cert.crt --output "hello.efi" "$HELLO_EFI" |
| 217 | + error_check "Couldn't sign $HELLO_EFI" |
| 218 | + if [ ! -f "hello.efi" ]; then |
| 219 | + error_exit "Couldn't create signed $TEST/hello.efi" |
| 220 | + fi |
| 221 | + else |
| 222 | + cp "$HELLO_EFI" hello.efi |
| 223 | + fi |
| 224 | + # remove everything except "cert.der" and "hello.efi" |
| 225 | + find -maxdepth 1 -type f ! -name "cert.der" ! -name "hello.efi" -exec rm {} \; |
| 226 | + error_check "find error" |
| 227 | + |
| 228 | + popd >/dev/null || error_exit "Couldn't popd" |
| 229 | +} |
| 230 | + |
| 231 | +# create_provisioning_test <test_name> |
| 232 | +create_provisioning_test() { |
| 233 | + local TEST=$1 |
| 234 | + local COMMIT=b2c2716c20afa76575b431e0a4cfd126e6df766f |
| 235 | + local DB_CRT_HASH=80aea212df9d1855e00251d80d1b384f9e7d7c48c4d6491f5a346dd52b3c2260 |
| 236 | + local DB_KEY_HASH=da2bb57a51a7eb7f701c17d9f7e8ade7668fe204af5518e520580328f7e64231 |
| 237 | + mkdir "$TEST" |
| 238 | + |
| 239 | + pushd "$TEST" >/dev/null || error_exit "Couldn't enter $TEST" |
| 240 | + |
| 241 | + wget https://raw.githubusercontent.com/Wind-River/meta-secure-core/$COMMIT/meta-signing-key/files/uefi_sb_keys/DB.crt |
| 242 | + wget https://raw.githubusercontent.com/Wind-River/meta-secure-core/$COMMIT/meta-signing-key/files/uefi_sb_keys/DB.key |
| 243 | + if ! diff <(sha256sum DB.crt) <(echo "$DB_CRT_HASH DB.crt") || \ |
| 244 | + ! diff <(sha256sum DB.key) <(echo "$DB_KEY_HASH DB.key") |
| 245 | + then |
| 246 | + error_exit "Wrong DB.crt/DB.key sha256 hash" |
| 247 | + fi |
| 248 | + cp "$SCRIPTDIR/LockDown.efi" . |
| 249 | + sbsign --key DB.key --cert DB.crt --output "hello.efi" "$HELLO_EFI" |
| 250 | + # remove everything except "LockDown.efi" and "hello.efi" |
| 251 | + find -maxdepth 1 -type f ! -name "LockDown.efi" ! -name "hello.efi" -exec rm {} \; |
| 252 | + |
| 253 | + popd >/dev/null || error_exit "Couldn't popd" |
| 254 | +} |
| 255 | + |
| 256 | +# create_provisioning_kek_test <test_name> |
| 257 | +create_provisioning_kek_test() { |
| 258 | + local TEST=$1 |
| 259 | + local COMMIT=b2c2716c20afa76575b431e0a4cfd126e6df766f |
| 260 | + local KEK_CRT_HASH=1a67de100cfc909a1a84fc2444e8378a01fe1fecb2cd37a6b4634b10662a21d2 |
| 261 | + mkdir "$TEST" |
| 262 | + |
| 263 | + pushd "$TEST" >/dev/null || error_exit "Couldn't enter $TEST" |
| 264 | + |
| 265 | + wget https://raw.githubusercontent.com/Wind-River/meta-secure-core/$COMMIT/meta-signing-key/files/uefi_sb_keys/KEK.crt |
| 266 | + if ! diff <(sha256sum KEK.crt) <(echo "$KEK_CRT_HASH KEK.crt"); then |
| 267 | + error_exit "Wrong KEK.crt sha256 hash" |
| 268 | + fi |
| 269 | + openssl x509 -fingerprint -in KEK.crt -noout -text > KEK2.crt |
| 270 | + mv KEK2.crt KEK.crt |
| 271 | + |
| 272 | + popd >/dev/null || error_exit "Couldn't popd" |
| 273 | +} |
| 274 | + |
| 275 | +cleanup() { |
| 276 | + echo "removing $TEMPDIR" |
| 277 | + rm -r "$TEMPDIR" |
| 278 | +} |
| 279 | + |
| 280 | +parse_args() { |
| 281 | + positional_args=() |
| 282 | + while [[ $# -gt 0 ]]; do |
| 283 | + case $1 in |
| 284 | + -v|--verbose) |
| 285 | + set -x |
| 286 | + ;; |
| 287 | + -*) |
| 288 | + print_usage |
| 289 | + error_exit "Unknown option $1" |
| 290 | + ;; |
| 291 | + *) |
| 292 | + positional_args+=("$1") |
| 293 | + ;; |
| 294 | + esac |
| 295 | + shift |
| 296 | + done |
| 297 | +} |
| 298 | + |
| 299 | +print_usage() { |
| 300 | + echo "generate-image.sh [-v]" |
| 301 | +} |
| 302 | + |
| 303 | +parse_args "$@" |
| 304 | +set -- "${positional_args[@]}" |
| 305 | + |
| 306 | +if [ $# -ne 0 ]; then |
| 307 | + print_usage |
| 308 | + error_exit "Script doesn't accept positional arguments" |
| 309 | +fi |
| 310 | + |
| 311 | +SCRIPTDIR=$(readlink -f "$(dirname "$0")") |
| 312 | +HELLO_EFI="$(realpath hello.efi)" |
| 313 | + |
| 314 | +TEMPDIR=$(mktemp -d) |
| 315 | +trap "cleanup" EXIT |
| 316 | + |
| 317 | +FILES="$TEMPDIR/files" |
| 318 | +mkdir "$FILES" |
| 319 | +pushd "$FILES" >/dev/null || error_check "Couldn't create $FILES" |
| 320 | + |
| 321 | +install_deps |
| 322 | + |
| 323 | +create_test SBO003.001 y create_rsa_key 2048 |
| 324 | +create_test SBO004.001 n create_rsa_key 2048 |
| 325 | +create_test SBO008.001 n create_bad_format_key 2048 |
| 326 | +create_test SBO009.001 y create_intermediate_key |
| 327 | +create_test SBO010.001 y create_rsa_key 2048 |
| 328 | +create_test SBO010.002 y create_rsa_key 3072 |
| 329 | +create_test SBO010.003 y create_rsa_key 4096 |
| 330 | +create_test SBO010.004 y create_ecdsa_key 256 |
| 331 | +create_test SBO010.005 y create_ecdsa_key 384 |
| 332 | +create_test SBO010.006 y create_ecdsa_key 521 |
| 333 | +create_test SBO011.001 y create_expired_cert |
| 334 | +create_provisioning_test SBO013.001 |
| 335 | +create_provisioning_kek_test SBO013.002 |
| 336 | + |
| 337 | +echo "creating iso image" |
| 338 | +create_iso |
| 339 | +cp "$IMAGEPATH" "$SCRIPTDIR"/ |
| 340 | +error_check "Couldn't copy $IMAGELABEL.img to $SCRIPTDIR" |
| 341 | +echo "Done" |
| 342 | +popd >/dev/null || error_check "Couldn't go back to original directory" |
0 commit comments