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