262 lines
8.0 KiB
Bash
262 lines
8.0 KiB
Bash
|
#!/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=${RSA4096_SHA512_ALGOID}
|
||
|
RECOVERY_KEY_ALGOID=${RSA4096_SHA512_ALGOID}
|
||
|
|
||
|
FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID}
|
||
|
DEV_FIRMWARE_DATAKEY_ALGOID=${RSA4096_SHA256_ALGOID}
|
||
|
|
||
|
RECOVERY_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID}
|
||
|
INSTALLER_KERNEL_ALGOID=${RSA4096_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}
|
||
|
}
|