1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
#!/bin/bash
# Copyright 2011 The ChromiumOS Authors
# 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.
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}
MINIOS_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID}
INSTALLER_KERNEL_ALGOID=${RSA4096_SHA512_ALGOID}
KERNEL_SUBKEY_ALGOID=${RSA4096_SHA256_ALGOID}
KERNEL_DATAKEY_ALGOID=${RSA2048_SHA256_ALGOID}
# AP RO Verification.
ARV_ROOT_ALGOID=${RSA4096_SHA256_ALGOID}
ARV_PLATFORM_ALGOID=${RSA4096_SHA256_ALGOID}
ARV_ROOT_NAME_BASE="arv_root"
# Presumably the script is run from the top of the PreMP keys directory
# tree, place AP RO verification root key there.
ARV_ROOT_DIR="ApRoV1Signing-PreMP"
# Keyblock modes determine which boot modes a signing key is valid for use
# in verification.
# !DEV 0x1 DEV 0x2
# !REC 0x4 REC 0x8
# !MINIOS 0x10 MINIOS 0x20
# Note that firmware keyblock modes are not used. Consider deprecating.
# Only allow RW firmware in non-recovery + non-miniOS.
FIRMWARE_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10))
# Only allow in dev mode + non-recovery + non-miniOS.
DEV_FIRMWARE_KEYBLOCK_MODE=$((0x2 | 0x4 | 0x10))
# Only allow in recovery mode + non-miniOS.
RECOVERY_KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x8 | 0x10))
# Only allow in recovery mode + miniOS.
MINIOS_KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x8 | 0x20))
# Only allow in non-recovery + non-miniOS.
KERNEL_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10))
# Only allow in dev + recovery + non-miniOS.
INSTALLER_KERNEL_KEYBLOCK_MODE=$((0x2 | 0x8 | 0x10))
# Only allow in non-recovery + non-miniOS, does not mean much for AP RO keys.
ARV_KEYBLOCK_MODE=$((0x1 | 0x2 | 0x4 | 0x10))
# 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"
}
# Used to generate keys for signing update payloads.
make_au_payload_key() {
local dir=$1
local priv="${dir}/update_key.pem"
local pub="${dir}/update-payload-key-pub.pem"
openssl genrsa -out "${priv}" 2048
openssl rsa -pubout -in "${priv}" -out "${pub}"
}
# 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
# 0x10 Not miniOS mode
# 0x20 miniOS mode
make_keyblock() {
local base=$1
local flags=$2
local pubkey=$3
# (Local) path to the key we're using to sign the keyblock.
# This is required, since the public key (as specified with --signpubkey)
# must always be local.
local signkey_path=$4
# Remote URI to the key we're using to sign the keyblock.
# Optional, if not set we'll look for the private key in signkey_path.
local signkey_uri=$5
local signkey_priv="${signkey_path}.vbprivk"
# If the URI is set, the private key is remote.
if [[ -n "${signkey_uri}" ]]; then
signkey_priv="${signkey_uri}"
fi
echo "creating $base keyblock..."
# create it
vbutil_keyblock \
--pack "${base}.keyblock" \
--flags $flags \
--datapubkey "${pubkey}.vbpubk" \
--signprivate "${signkey_priv}"
# verify it
vbutil_keyblock \
--unpack "${base}.keyblock" \
--signpubkey "${signkey_path}.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}
}
# Create a new ed25519 key pair given a base name. For example, if the
# base is "dir/foo", this will create "dir/foo.priv.pem" and
# "dir/foo.pub.pem".
# Args: BASE
generate_ed25519_key() {
local base="$1"
# Generate ed25519 private and public key.
openssl genpkey -algorithm Ed25519 -out "${base}.priv.pem"
openssl pkey -in "${base}.priv.pem" -pubout -text_pub -out "${base}.pub.pem"
}
|