1#!/bin/bash 2# Copyright 2011 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 key generation functions. 7 8SCRIPT_DIR="$(dirname "$(readlink -f -- "$0")")" 9PROG=$(basename "$0") 10CROS_LOG_PREFIX="${PROG}: " 11 12# Prints an informational message. 13info() { 14 echo "${CROS_LOG_PREFIX}INFO: $*" >&2 15} 16 17# Prints a warning message. 18warn() { 19 echo "${CROS_LOG_PREFIX}WARNING: $*" >&2 20} 21 22# Prints an error message. 23error() { 24 echo "${CROS_LOG_PREFIX}ERROR: $*" >&2 25} 26 27# Print an error message and then exit the script. 28die() { 29 error "$@" 30 exit 1 31} 32 33# Algorithm ID mappings: 34RSA1024_SHA1_ALGOID=0 35RSA1024_SHA256_ALGOID=1 36RSA1024_SHA512_ALGOID=2 37RSA2048_SHA1_ALGOID=3 38RSA2048_SHA256_ALGOID=4 39RSA2048_SHA512_ALGOID=5 40RSA4096_SHA1_ALGOID=6 41RSA4096_SHA256_ALGOID=7 42RSA4096_SHA512_ALGOID=8 43RSA8192_SHA1_ALGOID=9 44RSA8192_SHA256_ALGOID=10 45RSA8192_SHA512_ALGOID=11 46alg_to_keylen() { 47 echo $(( 1 << (10 + ($1 / 3)) )) 48} 49 50# Default algorithms. 51ROOT_KEY_ALGOID=${RSA4096_SHA512_ALGOID} 52RECOVERY_KEY_ALGOID=${RSA4096_SHA512_ALGOID} 53 54FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID} 55DEV_FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID} 56 57RECOVERY_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID} 58MINIOS_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID} 59INSTALLER_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID} 60KERNEL_SUBKEY_ALGOID=${RSA4096_SHA256_ALGOID} 61KERNEL_DATAKEY_ALGOID=${RSA2048_SHA256_ALGOID} 62 63# AP RO Verification. 64ARV_ROOT_ALGOID=${RSA4096_SHA256_ALGOID} 65ARV_PLATFORM_ALGOID=${RSA4096_SHA256_ALGOID} 66ARV_ROOT_NAME_BASE="arv_root" 67# Presumably the script is run from the top of the PreMP keys directory 68# tree, place AP RO verification root key there. 69ARV_ROOT_DIR="ApRoV1Signing-PreMP" 70 71# Keyblock modes determine which boot modes a signing key is valid for use 72# in verification. 73# !DEV 0x1 DEV 0x2 74# !REC 0x4 REC 0x8 75# !MINIOS 0x10 MINIOS 0x20 76# Note that firmware keyblock modes are not used. Consider deprecating. 77 78# Only allow RW firmware in non-recovery + non-miniOS. 79FIRMWARE_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10)) 80# Only allow in dev mode + non-recovery + non-miniOS. 81DEV_FIRMWARE_KEYBLOCK_MODE=$((0x2 | 0x4 | 0x10)) 82# Only allow in recovery mode + non-miniOS. 83RECOVERY_KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x8 | 0x10)) 84# Only allow in recovery mode + miniOS. 85MINIOS_KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x8 | 0x20)) 86# Only allow in non-recovery + non-miniOS. 87KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10)) 88# Only allow in dev + recovery + non-miniOS. 89INSTALLER_KERNEL_KEYBLOCK_MODE=$((0x2 | 0x8 | 0x10)) 90# Only allow in non-recovery + non-miniOS, does not mean much for AP RO keys. 91ARV_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10)) 92 93 94# Emit .vbpubk and .vbprivk using given basename and algorithm 95# NOTE: This function also appears in ../../utility/dev_make_keypair. Making 96# the two implementations the same would require some common.sh, which is more 97# likely to cause problems than just keeping an eye out for any differences. If 98# you feel the need to change this file, check the history of that other file 99# to see what may need updating here too. 100make_pair() { 101 local base=$1 102 local alg=$2 103 local key_version=${3:-1} 104 local len=$(alg_to_keylen $alg) 105 106 echo "creating $base keypair (version = $key_version)..." 107 108 # make the RSA keypair 109 openssl genrsa -F4 -out "${base}_${len}.pem" $len 110 # create a self-signed certificate 111 openssl req -batch -new -x509 -key "${base}_${len}.pem" \ 112 -out "${base}_${len}.crt" 113 # generate pre-processed RSA public key 114 dumpRSAPublicKey -cert "${base}_${len}.crt" > "${base}_${len}.keyb" 115 116 # wrap the public key 117 vbutil_key \ 118 --pack "${base}.vbpubk" \ 119 --key "${base}_${len}.keyb" \ 120 --version "${key_version}" \ 121 --algorithm $alg 122 123 # wrap the private key 124 vbutil_key \ 125 --pack "${base}.vbprivk" \ 126 --key "${base}_${len}.pem" \ 127 --algorithm $alg 128 129 # remove intermediate files 130 rm -f "${base}_${len}.pem" "${base}_${len}.crt" "${base}_${len}.keyb" 131} 132 133# Used to generate keys for signing update payloads. 134make_au_payload_key() { 135 local dir=$1 136 local priv="${dir}/update_key.pem" 137 local pub="${dir}/update-payload-key-pub.pem" 138 openssl genrsa -out "${priv}" 2048 139 openssl rsa -pubout -in "${priv}" -out "${pub}" 140} 141 142# Emit a .keyblock containing flags and a public key, signed by a private key 143# flags are the bitwise OR of these (passed in decimal, though) 144# 0x01 Developer switch off 145# 0x02 Developer switch on 146# 0x04 Not recovery mode 147# 0x08 Recovery mode 148# 0x10 Not miniOS mode 149# 0x20 miniOS mode 150make_keyblock() { 151 local base=$1 152 local flags=$2 153 local pubkey=$3 154 # (Local) path to the key we're using to sign the keyblock. 155 # This is required, since the public key (as specified with --signpubkey) 156 # must always be local. 157 local signkey_path=$4 158 # Remote URI to the key we're using to sign the keyblock. 159 # Optional, if not set we'll look for the private key in signkey_path. 160 local signkey_uri=$5 161 162 local signkey_priv="${signkey_path}.vbprivk" 163 # If the URI is set, the private key is remote. 164 if [[ -n "${signkey_uri}" ]]; then 165 signkey_priv="${signkey_uri}" 166 fi 167 168 echo "creating $base keyblock..." 169 170 # create it 171 vbutil_keyblock \ 172 --pack "${base}.keyblock" \ 173 --flags $flags \ 174 --datapubkey "${pubkey}.vbpubk" \ 175 --signprivate "${signkey_priv}" 176 177 # verify it 178 vbutil_keyblock \ 179 --unpack "${base}.keyblock" \ 180 --signpubkey "${signkey_path}.vbpubk" 181} 182 183# File to read current versions from. 184VERSION_FILE="key.versions" 185 186# ARGS: <VERSION_TYPE> [VERSION_FILE] 187get_version() { 188 local key="$1" 189 local file="${2:-${VERSION_FILE}}" 190 awk -F= -vkey="${key}" '$1 == key { print $NF }' "${file}" 191} 192 193# Loads the current versions prints them to stdout and sets the global version 194# variables: CURR_FIRMKEY_VER CURR_FIRM_VER CURR_KERNKEY_VER CURR_KERN_VER 195load_current_versions() { 196 local key_dir=$1 197 local VERSION_FILE="${key_dir}/${VERSION_FILE}" 198 if [[ ! -f ${VERSION_FILE} ]]; then 199 return 1 200 fi 201 CURR_FIRMKEY_VER=$(get_version "firmware_key_version") 202 # Firmware version is the kernel subkey version. 203 CURR_FIRM_VER=$(get_version "firmware_version") 204 # Kernel data key version is the kernel key version. 205 CURR_KERNKEY_VER=$(get_version "kernel_key_version") 206 CURR_KERN_VER=$(get_version "kernel_version") 207 208 cat <<EOF 209Current Firmware key version: ${CURR_FIRMKEY_VER} 210Current Firmware version: ${CURR_FIRM_VER} 211Current Kernel key version: ${CURR_KERNKEY_VER} 212Current Kernel version: ${CURR_KERN_VER} 213EOF 214} 215 216# Make backups of existing kernel subkeys and keyblocks that will be revved. 217# Backup format: 218# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock 219# Args: SUBKEY_VERSION DATAKEY_VERSION 220backup_existing_kernel_keyblock() { 221 if [[ ! -e kernel.keyblock ]]; then 222 return 223 fi 224 mv --no-clobber kernel.{keyblock,"v$2.v$1.keyblock"} 225} 226 227# Make backups of existing kernel subkeys and keyblocks that will be revved. 228# Backup format: 229# for keys: <key_name>.v<version>.vb{pub|priv}k 230# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock 231# Args: SUBKEY_VERSION DATAKEY_VERSION 232backup_existing_kernel_subkeys() { 233 local subkey_ver=$1 234 local datakey_ver=$2 235 # --no-clobber to prevent accidentally overwriting existing 236 # backups. 237 mv --no-clobber kernel_subkey.{vbprivk,"v${subkey_ver}.vbprivk"} 238 mv --no-clobber kernel_subkey.{vbpubk,"v${subkey_ver}.vbpubk"} 239 backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver} 240} 241 242# Make backups of existing kernel data keys and keyblocks that will be revved. 243# Backup format: 244# for keys: <key_name>.v<version>.vb{pub|priv}k 245# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock 246# Args: SUBKEY_VERSION DATAKEY_VERSION 247backup_existing_kernel_data_keys() { 248 local subkey_ver=$1 249 local datakey_ver=$2 250 # --no-clobber to prevent accidentally overwriting existing 251 # backups. 252 mv --no-clobber kernel_data_key.{vbprivk,"v${datakey_ver}.vbprivk"} 253 mv --no-clobber kernel_data_key.{vbpubk,"v${datakey_ver}.vbpubk"} 254 backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver} 255} 256 257# Make backups of existing firmware keys and keyblocks that will be revved. 258# Backup format: 259# for keys: <key_name>.v<version>.vb{pub|priv}k 260# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock 261# Args: SUBKEY_VERSION DATAKEY_VERSION 262backup_existing_firmware_keys() { 263 local subkey_ver=$1 264 local datakey_ver=$2 265 mv --no-clobber firmware_data_key.{vbprivk,"v${subkey_ver}.vbprivk"} 266 mv --no-clobber firmware_data_key.{vbpubk,"v${subkey_ver}.vbpubk"} 267 mv --no-clobber firmware.{keyblock,"v${datakey_ver}.v${subkey_ver}.keyblock"} 268} 269 270 271# Write new key version file with the updated key versions. 272# Args: FIRMWARE_KEY_VERSION FIRMWARE_VERSION KERNEL_KEY_VERSION 273# KERNEL_VERSION 274write_updated_version_file() { 275 local firmware_key_version=$1 276 local firmware_version=$2 277 local kernel_key_version=$3 278 local kernel_version=$4 279 280 cat > ${VERSION_FILE} <<EOF 281firmware_key_version=${firmware_key_version} 282firmware_version=${firmware_version} 283kernel_key_version=${kernel_key_version} 284kernel_version=${kernel_version} 285EOF 286} 287 288# Returns the incremented version number of the passed in key from the version 289# file. The options are "firmware_key_version", "firmware_version", 290# "kernel_key_version", or "kernel_version". 291# ARGS: KEY_DIR <key_name> 292increment_version() { 293 local key_dir=$1 294 local VERSION_FILE="${key_dir}/${VERSION_FILE}" 295 local old_version=$(get_version $2) 296 local new_version=$(( ${old_version} + 1 )) 297 298 if [[ ${new_version} -gt 0xffff ]]; then 299 echo "Version overflow!" >&2 300 return 1 301 fi 302 echo ${new_version} 303} 304 305# Create a new ed25519 key pair given a base name. For example, if the 306# base is "dir/foo", this will create "dir/foo.priv.pem" and 307# "dir/foo.pub.pem". 308# Args: BASE 309generate_ed25519_key() { 310 local base="$1" 311 312 # Generate ed25519 private and public key. 313 openssl genpkey -algorithm Ed25519 -out "${base}.priv.pem" 314 openssl pkey -in "${base}.priv.pem" -pubout -text_pub -out "${base}.pub.pem" 315} 316