xref: /aosp_15_r20/external/vboot_reference/scripts/keygeneration/common.sh (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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