blob: 9acffcc9601a4258d26103549d6045e2118275ee [file] [log] [blame]
#!/bin/bash
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Common key generation functions.
SCRIPT_DIR="$(dirname "$(readlink -f -- "$0")")"
PROG=$(basename "$0")
CROS_LOG_PREFIX="${PROG}: "
# Prints an informational message.
info() {
echo "${CROS_LOG_PREFIX}INFO: $*" >&2
}
# Prints a warning message.
warn() {
echo "${CROS_LOG_PREFIX}WARNING: $*" >&2
}
# Prints an error message.
error() {
echo "${CROS_LOG_PREFIX}ERROR: $*" >&2
}
# Print an error message and then exit the script.
die() {
error "$@"
exit 1
}
# Algorithm ID mappings:
RSA1024_SHA1_ALGOID=0
RSA1024_SHA256_ALGOID=1
RSA1024_SHA512_ALGOID=2
RSA2048_SHA1_ALGOID=3
RSA2048_SHA256_ALGOID=4
RSA2048_SHA512_ALGOID=5
RSA4096_SHA1_ALGOID=6
RSA4096_SHA256_ALGOID=7
RSA4096_SHA512_ALGOID=8
RSA8192_SHA1_ALGOID=9
RSA8192_SHA256_ALGOID=10
RSA8192_SHA512_ALGOID=11
alg_to_keylen() {
echo $(( 1 << (10 + ($1 / 3)) ))
}
# Default algorithms.
EC_ROOT_KEY_ALGOID=${RSA4096_SHA256_ALGOID}
EC_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID}
ROOT_KEY_ALGOID=${RSA8192_SHA512_ALGOID}
RECOVERY_KEY_ALGOID=${RSA8192_SHA512_ALGOID}
FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID}
DEV_FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID}
RECOVERY_KERNEL_ALGOID=${RSA8192_SHA512_ALGOID}
INSTALLER_KERNEL_ALGOID=${RSA8192_SHA512_ALGOID}
KERNEL_SUBKEY_ALGOID=${RSA4096_SHA256_ALGOID}
KERNEL_DATAKEY_ALGOID=${RSA2048_SHA256_ALGOID}
# Keyblock modes determine which boot modes a signing key is valid for use
# in verification.
EC_KEYBLOCK_MODE=7 # Only allow RW EC firmware in non-recovery.
FIRMWARE_KEYBLOCK_MODE=7 # Only allow RW firmware in non-recovery.
DEV_FIRMWARE_KEYBLOCK_MODE=6 # Only allow in dev mode.
RECOVERY_KERNEL_KEYBLOCK_MODE=11 # Only in recovery mode.
KERNEL_KEYBLOCK_MODE=7 # Only allow in non-recovery.
INSTALLER_KERNEL_KEYBLOCK_MODE=10 # Only allow in Dev + Recovery.
# Emit .vbpubk and .vbprivk using given basename and algorithm
# NOTE: This function also appears in ../../utility/dev_make_keypair. Making
# the two implementations the same would require some common.sh, which is more
# likely to cause problems than just keeping an eye out for any differences. If
# you feel the need to change this file, check the history of that other file
# to see what may need updating here too.
make_pair() {
local base=$1
local alg=$2
local key_version=${3:-1}
local len=$(alg_to_keylen $alg)
echo "creating $base keypair (version = $key_version)..."
# make the RSA keypair
openssl genrsa -F4 -out "${base}_${len}.pem" $len
# create a self-signed certificate
openssl req -batch -new -x509 -key "${base}_${len}.pem" \
-out "${base}_${len}.crt"
# generate pre-processed RSA public key
dumpRSAPublicKey -cert "${base}_${len}.crt" > "${base}_${len}.keyb"
# wrap the public key
vbutil_key \
--pack "${base}.vbpubk" \
--key "${base}_${len}.keyb" \
--version "${key_version}" \
--algorithm $alg
# wrap the private key
vbutil_key \
--pack "${base}.vbprivk" \
--key "${base}_${len}.pem" \
--algorithm $alg
# remove intermediate files
rm -f "${base}_${len}.pem" "${base}_${len}.crt" "${base}_${len}.keyb"
}
# Emit a .keyblock containing flags and a public key, signed by a private key
# flags are the bitwise OR of these (passed in decimal, though)
# 0x01 Developer switch off
# 0x02 Developer switch on
# 0x04 Not recovery mode
# 0x08 Recovery mode
make_keyblock() {
local base=$1
local flags=$2
local pubkey=$3
local signkey=$4
echo "creating $base keyblock..."
# create it
vbutil_keyblock \
--pack "${base}.keyblock" \
--flags $flags \
--datapubkey "${pubkey}.vbpubk" \
--signprivate "${signkey}.vbprivk"
# verify it
vbutil_keyblock \
--unpack "${base}.keyblock" \
--signpubkey "${signkey}.vbpubk"
}
# File to read current versions from.
VERSION_FILE="key.versions"
# ARGS: <VERSION_TYPE> [VERSION_FILE]
get_version() {
local key="$1"
local file="${2:-${VERSION_FILE}}"
awk -F= -vkey="${key}" '$1 == key { print $NF }' "${file}"
}
# Loads the current versions prints them to stdout and sets the global version
# variables: CURR_FIRMKEY_VER CURR_FIRM_VER CURR_KERNKEY_VER CURR_KERN_VER
load_current_versions() {
local key_dir=$1
local VERSION_FILE="${key_dir}/${VERSION_FILE}"
if [[ ! -f ${VERSION_FILE} ]]; then
return 1
fi
CURR_FIRMKEY_VER=$(get_version "firmware_key_version")
# Firmware version is the kernel subkey version.
CURR_FIRM_VER=$(get_version "firmware_version")
# Kernel data key version is the kernel key version.
CURR_KERNKEY_VER=$(get_version "kernel_key_version")
CURR_KERN_VER=$(get_version "kernel_version")
cat <<EOF
Current Firmware key version: ${CURR_FIRMKEY_VER}
Current Firmware version: ${CURR_FIRM_VER}
Current Kernel key version: ${CURR_KERNKEY_VER}
Current Kernel version: ${CURR_KERN_VER}
EOF
}
# Make backups of existing kernel subkeys and keyblocks that will be revved.
# Backup format:
# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
# Args: SUBKEY_VERSION DATAKEY_VERSION
backup_existing_kernel_keyblock() {
if [[ ! -e kernel.keyblock ]]; then
return
fi
mv --no-clobber kernel.{keyblock,"v$2.v$1.keyblock"}
}
# Make backups of existing kernel subkeys and keyblocks that will be revved.
# Backup format:
# for keys: <key_name>.v<version>.vb{pub|priv}k
# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
# Args: SUBKEY_VERSION DATAKEY_VERSION
backup_existing_kernel_subkeys() {
local subkey_ver=$1
local datakey_ver=$2
# --no-clobber to prevent accidentally overwriting existing
# backups.
mv --no-clobber kernel_subkey.{vbprivk,"v${subkey_ver}.vbprivk"}
mv --no-clobber kernel_subkey.{vbpubk,"v${subkey_ver}.vbpubk"}
backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver}
}
# Make backups of existing kernel data keys and keyblocks that will be revved.
# Backup format:
# for keys: <key_name>.v<version>.vb{pub|priv}k
# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
# Args: SUBKEY_VERSION DATAKEY_VERSION
backup_existing_kernel_data_keys() {
local subkey_ver=$1
local datakey_ver=$2
# --no-clobber to prevent accidentally overwriting existing
# backups.
mv --no-clobber kernel_data_key.{vbprivk,"v${datakey_ver}.vbprivk"}
mv --no-clobber kernel_data_key.{vbpubk,"v${datakey_ver}.vbpubk"}
backup_existing_kernel_keyblock ${subkey_ver} ${datakey_ver}
}
# Make backups of existing firmware keys and keyblocks that will be revved.
# Backup format:
# for keys: <key_name>.v<version>.vb{pub|priv}k
# for keyblocks: <keyblock_name>.v<datakey version>.v<subkey version>.keyblock
# Args: SUBKEY_VERSION DATAKEY_VERSION
backup_existing_firmware_keys() {
local subkey_ver=$1
local datakey_ver=$2
mv --no-clobber firmware_data_key.{vbprivk,"v${subkey_ver}.vbprivk"}
mv --no-clobber firmware_data_key.{vbpubk,"v${subkey_ver}.vbpubk"}
mv --no-clobber firmware.{keyblock,"v${datakey_ver}.v${subkey_ver}.keyblock"}
}
# Write new key version file with the updated key versions.
# Args: FIRMWARE_KEY_VERSION FIRMWARE_VERSION KERNEL_KEY_VERSION
# KERNEL_VERSION
write_updated_version_file() {
local firmware_key_version=$1
local firmware_version=$2
local kernel_key_version=$3
local kernel_version=$4
cat > ${VERSION_FILE} <<EOF
firmware_key_version=${firmware_key_version}
firmware_version=${firmware_version}
kernel_key_version=${kernel_key_version}
kernel_version=${kernel_version}
EOF
}
# Returns the incremented version number of the passed in key from the version
# file. The options are "firmware_key_version", "firmware_version",
# "kernel_key_version", or "kernel_version".
# ARGS: KEY_DIR <key_name>
increment_version() {
local key_dir=$1
local VERSION_FILE="${key_dir}/${VERSION_FILE}"
local old_version=$(get_version $2)
local new_version=$(( ${old_version} + 1 ))
if [[ ${new_version} -gt 0xffff ]]; then
echo "Version overflow!" >&2
return 1
fi
echo ${new_version}
}