1#!/bin/bash 2# Copyright 2018 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6# Common UEFI key generation functions. 7 8# shellcheck source=../common.sh 9. "$(dirname "$0")/../common.sh" 10 11# Checks whether the given key directory name is "uefi". 12# Dies if it isn't. 13# ARGS: KEY_DIR 14check_uefi_key_dir_name() { 15 local key_dir="$1" 16 local key_dir_fullpath="$(readlink -f "${key_dir}")" 17 local key_dir_basename="$(basename "${key_dir_fullpath}")" 18 if [[ "${key_dir_basename}" != "uefi" ]]; then 19 die "Key directory base name is not \"uefi\"" 20 fi 21} 22 23# File to read current versions from. 24UEFI_VERSION_FILE="uefi_key.versions" 25 26# Prints the version value for the given VERSION_TYPE, from UEFI_VERSION_FILE. 27# ARGS: <VERSION_TYPE> [UEFI_VERSION_FILE] 28get_uefi_version() { 29 local key="$1" 30 local file="${2:-${UEFI_VERSION_FILE}}" 31 awk -F= -vkey="${key}" '$1 == key { print $NF }' "${file}" 32} 33 34# Loads the current versions, prints them to stdout, and sets the global version 35# variables: CURR_PK_KEY_VER CURR_KEK_KEY_VER CURR_DB_KEY_VER 36# CURR_DB_CHILD_KEY_VER 37# ARGS: KEY_DIR 38load_current_uefi_key_versions() { 39 local key_dir="$1" 40 local UEFI_VERSION_FILE="${key_dir}/${UEFI_VERSION_FILE}" 41 if [[ ! -f "${UEFI_VERSION_FILE}" ]]; then 42 return 1 43 fi 44 CURR_PK_KEY_VER=$(get_uefi_version "pk_key_version") 45 CURR_KEK_KEY_VER=$(get_uefi_version "kek_key_version") 46 CURR_DB_KEY_VER=$(get_uefi_version "db_key_version") 47 CURR_DB_CHILD_KEY_VER=$(get_uefi_version "db_child_key_version") 48 49 cat <<EOF 50Current UEFI Platform Key (PK) version: ${CURR_PK_KEY_VER} 51Current UEFI Key Exchange Key (KEK) version: ${CURR_KEK_KEY_VER} 52Current UEFI DB key version: ${CURR_DB_KEY_VER} 53Current UEFI DB child key version: ${CURR_DB_CHILD_KEY_VER} 54EOF 55} 56 57# The common part for the subject of a UEFI key. 58_CHROMIUM_OS_SUBJECT=\ 59'/C=US/ST=California/L=Mountain View/O=Google LLC./OU=Chromium OS' 60 61# Prints a UEFI key subject. 62# ARGS: TITLE VERSION 63_get_subj() { 64 local title="$1" 65 local version="$2" 66 67 echo "${_CHROMIUM_OS_SUBJECT}/CN=${title} v${version}" 68} 69 70# Generates a pair of a private key and a self-signed cert at the current 71# directory. Generated files are 72# $1/$1.rsa: The private key 73# $1/$1.pem: The self-signed cert in PEM format 74# ARGS: KEY_NAME SUBJECT 75_make_self_signed_pair() { 76 local key_name="$1" 77 local subj="$2" 78 79 mkdir -p "${key_name}" 80 pushd "${key_name}" >/dev/null || return 1 81 openssl req -new -x509 -nodes -newkey rsa:2048 -sha256 \ 82 -keyout "${key_name}.rsa" -out "${key_name}.pem" \ 83 -subj "${subj}" -days 3650 84 popd >/dev/null 85} 86 87# Generates a pair of a private key and a cert signed by the given CA. 88# "$1" (the first argument) is the CA file name without extension. 89# The results are signed by "$1/$1.{rsa,pem}", and are generated in 90# "$1/$1.children" directory under the current directory. Generated files are 91# $1/$1.children/$2.rsa: The private key 92# $1/$1.children/$2.csr: The Certificate Signing Request 93# $1/$1.children/$2.pem: The certificate signed by "$1.{rsa,pem}" 94# ARGS: CA_NAME CHILD_KEY_NAME SUBJECT 95_make_child_pair() { 96 local ca_name="$1" # Base filename without extension. 97 local child_key_name="$2" 98 local subj="$3" 99 100 mkdir -p "${ca_name}/${ca_name}.children" 101 pushd "${ca_name}/${ca_name}.children" >/dev/null || return 1 102 openssl req -new -nodes -newkey rsa:2048 -sha256 \ 103 -keyout "${child_key_name}.rsa" -out "${child_key_name}.csr" \ 104 -subj "${subj}" 105 openssl x509 -req -sha256 -CA "../${ca_name}.pem" -CAkey "../${ca_name}.rsa" \ 106 -CAcreateserial -in "${child_key_name}.csr" \ 107 -out "${child_key_name}.pem" -days 3650 108 popd >/dev/null 109} 110 111# Makes a PK (Platform Key) keypair. 112# Generated files are 113# pk/pk.rsa: The private key 114# pk/pk.pem: The self-signed cert in PEM format 115# ARGS: VERSION 116make_pk_keypair() { 117 local version="$1" 118 _make_self_signed_pair pk \ 119 "$(_get_subj "UEFI Platform Key" "${version}")" 120} 121 122# Makes a KEK (Key Exchange Key) keypair. 123# Generated files are 124# kek/kek.rsa: The private key 125# kek/kek.pem: The self-signed cert in PEM format 126# ARGS: VERSION 127make_kek_keypair() { 128 local version="$1" 129 _make_self_signed_pair kek \ 130 "$(_get_subj "UEFI Key Exchange Key" "${version}")" 131} 132 133# Makes a DB keypair. 134# Generated files are 135# db/db.rsa: The private key 136# db/db.pem: The self-signed cert in PEM format 137# ARGS: VERSION 138make_db_keypair() { 139 local version="$1" 140 _make_self_signed_pair db \ 141 "$(_get_subj "UEFI DB Key" "${version}")" 142} 143 144# Makes a DB child keypair (a keypair signed by the db key). 145# Generated files are 146# db/db.children/db_child.rsa: The private key 147# db/db.children/db_child.csr: The Certificate Signing Request 148# db/db.children/db_child.pem: The certificate signed by "db/db.{rsa,pem}" 149# ARGS: DB_KEY_VERSION CHILD_KEY_VERSION 150make_db_child_keypair() { 151 local db_key_version="$1" 152 local child_key_version="$2" 153 _make_child_pair db db_child \ 154 "$(_get_subj "UEFI DB Child Key" \ 155 "${db_key_version}.${child_key_version}")" 156} 157 158# Makes a backup of a self-signed keypair. 159# ARGS: KEY_NAME VERSION 160_backup_self_signed_pair() { 161 local key_name="$1" 162 local version="$2" 163 pushd "${key_name}" >/dev/null || return 1 164 mv --no-clobber "${key_name}".{rsa,"v${version}.rsa"} 165 mv --no-clobber "${key_name}".{pem,"v${version}.pem"} 166 popd >/dev/null 167} 168 169# Makes a backup of a self-signed keypair and its child keys. 170# ARGS: KEY_NAME VERSION 171_backup_self_signed_pair_and_children() { 172 local key_name="$1" 173 local version="$2" 174 _backup_self_signed_pair "${key_name}" "${version}" 175 pushd "${key_name}" >/dev/null || return 1 176 mv --no-clobber "${key_name}".{children,"v${version}.children"} 177 popd >/dev/null 178} 179 180# Makes a backup of a child keypair signed by a CA. 181# ARGS: CA_NAME CHILD_KEY_NAME CHILD_KEY_VERSION 182_backup_child_pair() { 183 local ca_name="$1" 184 local child_key_name="$2" 185 local child_key_version="$3" 186 pushd "${ca_name}/${ca_name}.children" >/dev/null || return 1 187 mv --no-clobber "${child_key_name}".{rsa,"v${child_key_version}.rsa"} 188 mv --no-clobber "${child_key_name}".{csr,"v${child_key_version}.csr"} 189 mv --no-clobber "${child_key_name}".{pem,"v${child_key_version}.pem"} 190 popd >/dev/null 191} 192 193# Makes a backup of the PK (Platform Key) keypair. 194# Backup format: pk.v<pk key version>.{rsa,pem} 195# ARGS: PK_KEY_VERSION 196backup_pk_keypair() { 197 local pk_key_version="$1" 198 _backup_self_signed_pair pk "${pk_key_version}" 199} 200 201# Makes a backup of the KEK (Key Exchange Key) keypair. 202# Backup format: kek.v<kek key version>.{rsa,pem} 203# ARGS: KEK_KEY_VERSION 204backup_kek_keypair() { 205 local kek_key_version="$1" 206 _backup_self_signed_pair kek "${kek_key_version}" 207} 208 209# Makes a backup of the DB keypair and its children. 210# Backup format: 211# for db keypair: db.v<db key version>.{rsa,pem} 212# for child keypair: db.v<db key version>.childern/child*.{rsa,csr,pem} 213# ARGS: DB_KEY_VERSION 214backup_db_keypair_and_children() { 215 local db_key_version="$1" 216 _backup_self_signed_pair_and_children db "${db_key_version}" 217} 218 219# Makes a backup of the DB child keypair. 220# Backup format: db.children/child.v<db child key version>.{rsa,csr,pem} 221# ARGS: DB_CHILD_KEY_VERSION 222backup_db_child_keypair() { 223 local db_child_key_version="$1" 224 _backup_child_pair db db_child "${db_child_key_version}" 225} 226 227# Writes new key version file with the updated key versions. 228# Args: PK_KEY_VERSION KEK_KEY_VERSION DB_KEY_VERSION DB_CHILD_KEY_VERSION 229write_updated_uefi_version_file() { 230 local pk_key_version="$1" 231 local kek_key_version="$2" 232 local db_key_version="$3" 233 local db_child_key_version="$4" 234 235 cat > "${UEFI_VERSION_FILE}" <<EOF 236pk_key_version=${pk_key_version} 237kek_key_version=${kek_key_version} 238db_key_version=${db_key_version} 239db_child_key_version=${db_child_key_version} 240EOF 241} 242 243# Returns the incremented version number of the passed in key from the version 244# file. The options are "pk_key_version", "kek_key_version", "db_key_version", 245# or "db_child_key_version". 246# ARGS: KEY_DIR <key_name> 247increment_uefi_version() { 248 local key_dir="$1" 249 local UEFI_VERSION_FILE="${key_dir}/${UEFI_VERSION_FILE}" 250 local old_version=$(get_uefi_version "$2") 251 local new_version=$(( old_version + 1 )) 252 253 echo "${new_version}" 254} 255