1#!/bin/bash
2# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
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 "$0")"
9
10# 0 = (RSA1024 SHA1)
11# 1 = (RSA1024 SHA256)
12# 2 = (RSA1024 SHA512)
13# 3 = (RSA2048 SHA1)
14# 4 = (RSA2048 SHA256)
15# 5 = (RSA2048 SHA512)
16# 6 = (RSA4096 SHA1)
17# 7 = (RSA4096 SHA256)
18# 8 = (RSA4096 SHA512)
19# 9 = (RSA8192 SHA1)
20# 10 = (RSA8192 SHA256)
21# 11 = (RSA8192 SHA512)
22function alg_to_keylen {
23  echo $(( 1 << (10 + ($1 / 3)) ))
24}
25
26# Default algorithms.
27EC_ROOT_KEY_ALGOID=7
28EC_DATAKEY_ALGOID=7
29
30ROOT_KEY_ALGOID=11
31RECOVERY_KEY_ALGOID=11
32
33FIRMWARE_DATAKEY_ALGOID=7
34DEV_FIRMWARE_DATAKEY_ALGOID=7
35
36RECOVERY_KERNEL_ALGOID=11
37INSTALLER_KERNEL_ALGOID=11
38KERNEL_SUBKEY_ALGOID=7
39KERNEL_DATAKEY_ALGOID=4
40
41# Keyblock modes determine which boot modes a signing key is valid for use
42# in verification.
43EC_KEYBLOCK_MODE=7  # Only allow RW EC firmware in non-recovery.
44FIRMWARE_KEYBLOCK_MODE=7  # Only allow RW firmware in non-recovery.
45DEV_FIRMWARE_KEYBLOCK_MODE=6  # Only allow in dev mode.
46RECOVERY_KERNEL_KEYBLOCK_MODE=11 # Only in recovery mode.
47KERNEL_KEYBLOCK_MODE=7  # Only allow in non-recovery.
48INSTALLER_KERNEL_KEYBLOCK_MODE=10  # Only allow in Dev + Recovery.
49
50# Emit .vbpubk and .vbprivk using given basename and algorithm
51# NOTE: This function also appears in ../../utility/dev_make_keypair. Making
52# the two implementations the same would require some common.sh, which is more
53# likely to cause problems than just keeping an eye out for any differences. If
54# you feel the need to change this file, check the history of that other file
55# to see what may need updating here too.
56function make_pair {
57  local base=$1
58  local alg=$2
59  local key_version=${3:-1}
60  local len=$(alg_to_keylen $alg)
61
62  echo "creating $base keypair (version = $key_version)..."
63
64  # make the RSA keypair
65  openssl genrsa -F4 -out "${base}_${len}.pem" $len
66  # create a self-signed certificate
67  openssl req -batch -new -x509 -key "${base}_${len}.pem" \
68    -out "${base}_${len}.crt"
69  # generate pre-processed RSA public key
70  dumpRSAPublicKey -cert "${base}_${len}.crt" > "${base}_${len}.keyb"
71
72  # wrap the public key
73  vbutil_key \
74    --pack "${base}.vbpubk" \
75    --key "${base}_${len}.keyb" \
76    --version  "${key_version}" \
77    --algorithm $alg
78
79  # wrap the private key
80  vbutil_key \
81    --pack "${base}.vbprivk" \
82    --key "${base}_${len}.pem" \
83    --algorithm $alg
84
85  # remove intermediate files
86  rm -f "${base}_${len}.pem" "${base}_${len}.crt" "${base}_${len}.keyb"
87}
88
89
90# Emit a .keyblock containing flags and a public key, signed by a private key
91# flags are the bitwise OR of these (passed in decimal, though)
92#   0x01  Developer switch off
93#   0x02  Developer switch on
94#   0x04  Not recovery mode
95#   0x08  Recovery mode
96function make_keyblock {
97  local base=$1
98  local flags=$2
99  local pubkey=$3
100  local signkey=$4
101
102  echo "creating $base keyblock..."
103
104  # create it
105  vbutil_keyblock \
106    --pack "${base}.keyblock" \
107    --flags $flags \
108    --datapubkey "${pubkey}.vbpubk" \
109    --signprivate "${signkey}.vbprivk"
110
111  # verify it
112  vbutil_keyblock \
113    --unpack "${base}.keyblock" \
114    --signpubkey "${signkey}.vbpubk"
115}
116
117# File to read current versions from.
118VERSION_FILE="key.versions"
119
120# ARGS: <VERSION_TYPE> [VERSION_FILE]
121get_version() {
122  awk -F= '/^'$1'\>/ { print $NF }' "${2:-${VERSION_FILE}}"
123}
124
125# Loads the current versions prints them to stdout and sets the global version
126# variables: CURR_FIRMKEY_VER CURR_FIRM_VER CURR_KERNKEY_VER CURR_KERN_VER
127load_current_versions() {
128  local key_dir=$1
129  local VERSION_FILE="${key_dir}/${VERSION_FILE}"
130  if [[ ! -f ${VERSION_FILE} ]]; then
131    return 1
132  fi
133  CURR_FIRMKEY_VER=$(get_version "firmware_key_version")
134  # Firmware version is the kernel subkey version.
135  CURR_FIRM_VER=$(get_version "firmware_version")
136  # Kernel data key version is the kernel key version.
137  CURR_KERNKEY_VER=$(get_version "kernel_key_version")
138  CURR_KERN_VER=$(get_version "kernel_version")
139
140  cat <<EOF
141Current Firmware key version: ${CURR_FIRMKEY_VER}
142Current Firmware version: ${CURR_FIRM_VER}
143Current Kernel key version: ${CURR_KERNKEY_VER}
144Current Kernel version: ${CURR_KERN_VER}
145EOF
146}
147
148# Make backups of existing kernel subkeys and keyblocks that will be revved.
149# Backup format:
150# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
151# Args: SUBKEY_VERSION DATAKEY_VERSION
152backup_existing_kernel_keyblock() {
153  if [[ ! -e kernel.keyblock ]]; then
154    return
155  fi
156  mv --no-clobber kernel.{keyblock,"v$2.v$1.keyblock"}
157}
158
159# Make backups of existing kernel subkeys and keyblocks that will be revved.
160# Backup format:
161# for keys: <key_name>.v<version>.vb{pub|priv}k
162# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
163# Args: SUBKEY_VERSION DATAKEY_VERSION
164backup_existing_kernel_subkeys() {
165  local subkey_ver=$1
166  local datakey_ver=$2
167  # --no-clobber to prevent accidentally overwriting existing
168  # backups.
169  mv --no-clobber kernel_subkey.{vbprivk,"v${subkey_ver}.vbprivk"}
170  mv --no-clobber kernel_subkey.{vbpubk,"v${subkey_ver}.vbpubk"}
171  backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver}
172}
173
174# Make backups of existing kernel data keys and keyblocks that will be revved.
175# Backup format:
176# for keys: <key_name>.v<version>.vb{pub|priv}k
177# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
178# Args: SUBKEY_VERSION DATAKEY_VERSION
179backup_existing_kernel_data_keys() {
180  local subkey_ver=$1
181  local datakey_ver=$2
182  # --no-clobber to prevent accidentally overwriting existing
183  # backups.
184  mv --no-clobber kernel_data_key.{vbprivk,"v${datakey_ver}.vbprivk"}
185  mv --no-clobber kernel_data_key.{vbpubk,"v${datakey_ver}.vbpubk"}
186  backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver}
187}
188
189# Make backups of existing firmware keys and keyblocks that will be revved.
190# Backup format:
191# for keys: <key_name>.v<version>.vb{pub|priv}k
192# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
193# Args: SUBKEY_VERSION DATAKEY_VERSION
194backup_existing_firmware_keys() {
195  local subkey_ver=$1
196  local datakey_ver=$2
197  mv --no-clobber firmware_data_key.{vbprivk,"v${subkey_ver}.vbprivk"}
198  mv --no-clobber firmware_data_key.{vbpubk,"v${subkey_ver}.vbpubk"}
199  mv --no-clobber firmware.{keyblock,"v${datakey_ver}.v${subkey_ver}.keyblock"}
200}
201
202
203# Write new key version file with the updated key versions.
204# Args: FIRMWARE_KEY_VERSION FIRMWARE_VERSION KERNEL_KEY_VERSION
205#       KERNEL_VERSION
206write_updated_version_file() {
207  local firmware_key_version=$1
208  local firmware_version=$2
209  local kernel_key_version=$3
210  local kernel_version=$4
211
212  cat > ${VERSION_FILE} <<EOF
213firmware_key_version=${firmware_key_version}
214firmware_version=${firmware_version}
215kernel_key_version=${kernel_key_version}
216kernel_version=${kernel_version}
217EOF
218}
219
220# Returns the incremented version number of the passed in key from the version
221# file.  The options are "firmware_key_version", "firmware_version",
222# "kernel_key_version", or "kernel_version".
223# ARGS: KEY_DIR <key_name>
224increment_version() {
225  local key_dir=$1
226  local VERSION_FILE="${key_dir}/${VERSION_FILE}"
227  local old_version=$(get_version $2)
228  local new_version=$(( ${old_version} + 1 ))
229
230  if [[ ${new_version} -gt 0xffff ]]; then
231    echo "Version overflow!" >&2
232    return 1
233  fi
234  echo ${new_version}
235}
236