summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--futility/cmd_update.c131
-rwxr-xr-xtests/futility/test_update.sh14
2 files changed, 145 insertions, 0 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index ed0b83f2..964f071e 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -12,11 +12,14 @@
#include <stdio.h>
#include <stdlib.h>
+#include "2rsa.h"
#include "crossystem.h"
#include "fmap.h"
#include "futility.h"
#include "host_misc.h"
#include "utility.h"
+#include "util_misc.h"
+#include "vb2_common.h"
#include "vb2_struct.h"
#define COMMAND_BUFFER_SIZE 256
@@ -935,6 +938,22 @@ static int check_compatible_platform(struct updater_config *cfg)
}
/*
+ * Returns a valid root key from GBB header, or NULL on failure.
+ */
+static const struct vb2_packed_key *get_rootkey(
+ const struct vb2_gbb_header *gbb)
+{
+ struct vb2_packed_key *key = NULL;
+
+ key = (struct vb2_packed_key *)((uint8_t *)gbb + gbb->rootkey_offset);
+ if (!packed_key_looks_ok(key, gbb->rootkey_size)) {
+ Error("%s: Invalid root key.\n", __FUNCTION__);
+ return NULL;
+ }
+ return key;
+}
+
+/*
* Returns a key block key from given image section, or NULL on failure.
*/
static const struct vb2_keyblock *get_keyblock(
@@ -954,6 +973,57 @@ static const struct vb2_keyblock *get_keyblock(
}
/*
+ * Duplicates a key block and returns the duplicated block.
+ * The caller must free the returned key block after being used.
+ */
+static struct vb2_keyblock *dupe_keyblock(const struct vb2_keyblock *block)
+{
+ struct vb2_keyblock *new_block;
+
+ new_block = (struct vb2_keyblock *)malloc(block->keyblock_size);
+ assert(new_block);
+ memcpy(new_block, block, block->keyblock_size);
+ return new_block;
+}
+
+/*
+ * Verifies if keyblock is signed with given key.
+ * Returns 0 on success, otherwise failure.
+ */
+static int verify_keyblock(const struct vb2_keyblock *block,
+ const struct vb2_packed_key *sign_key) {
+ int r;
+ uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+ struct vb2_workbuf wb;
+ struct vb2_public_key key;
+ struct vb2_keyblock *new_block;
+
+ if (block->keyblock_signature.sig_size == 0) {
+ Error("%s: Keyblock is not signed.\n", __FUNCTION__);
+ return -1;
+ }
+ vb2_workbuf_init(&wb, workbuf, sizeof(workbuf));
+ if (VB2_SUCCESS != vb2_unpack_key(&key, sign_key)) {
+ Error("%s: Invalid signing key,\n", __FUNCTION__);
+ return -1;
+ }
+
+ /*
+ * vb2_verify_keyblock will destroy the signature inside keyblock
+ * so we have to verify with a local copy.
+ */
+ new_block = dupe_keyblock(block);
+ r = vb2_verify_keyblock(new_block, new_block->keyblock_size, &key, &wb);
+ free(new_block);
+
+ if (r != VB2_SUCCESS) {
+ Error("%s: Error verifying key block.\n", __FUNCTION__);
+ return -1;
+ }
+ return 0;
+}
+
+/*
* Gets the data key and firmware version from a section on firmware image.
* The section should contain a vb2_keyblock and a vb2_fw_preamble immediately
* after key block so we can decode and save the data key and firmware version
@@ -981,6 +1051,61 @@ static int get_key_versions(const struct firmware_image *image,
}
/*
+ * Checks if the root key in ro_image can verify vblocks in rw_image.
+ * Returns 0 for success, otherwise failure.
+ */
+static int check_compatible_root_key(const struct firmware_image *ro_image,
+ const struct firmware_image *rw_image)
+{
+ const struct vb2_gbb_header *gbb = find_gbb(ro_image);
+ const struct vb2_packed_key *rootkey;
+ const struct vb2_keyblock *keyblock;
+
+ if (!gbb)
+ return -1;
+
+ rootkey = get_rootkey(gbb);
+ if (!rootkey)
+ return -1;
+
+ /* Assume VBLOCK_A and VBLOCK_B are signed in same way. */
+ keyblock = get_keyblock(rw_image, FMAP_RW_VBLOCK_A);
+ if (!keyblock)
+ return -1;
+
+ if (verify_keyblock(keyblock, rootkey) != 0) {
+ const struct vb2_gbb_header *gbb_rw = find_gbb(rw_image);
+ const struct vb2_packed_key *rootkey_rw = NULL;
+ int is_same_key = 0;
+ /*
+ * Try harder to provide more info.
+ * packed_key_sha1_string uses static buffer so don't call
+ * it twice in args list of one expression.
+ */
+ if (gbb_rw)
+ rootkey_rw = get_rootkey(gbb_rw);
+ if (rootkey_rw) {
+ if (rootkey->key_offset == rootkey_rw->key_offset &&
+ rootkey->key_size == rootkey_rw->key_size &&
+ memcmp(rootkey, rootkey_rw, rootkey->key_size +
+ rootkey->key_offset) == 0)
+ is_same_key = 1;
+ }
+ printf("Current (RO) image root key is %s, ",
+ packed_key_sha1_string(rootkey));
+ if (is_same_key)
+ printf("same with target (RW) image. "
+ "Maybe RW corrupted?\n");
+ else
+ printf("target (RW) image is signed with rootkey %s.\n",
+ rootkey_rw ? packed_key_sha1_string(rootkey_rw) :
+ "<invalid>");
+ return -1;
+ }
+ return 0;
+}
+
+/*
* Checks if the given firmware image is signed with a key that won't be
* blocked by TPM's anti-rollback detection.
* Returns 0 for success, otherwise failure.
@@ -1030,6 +1155,7 @@ enum updater_error_codes {
UPDATE_ERR_WRITE_FIRMWARE,
UPDATE_ERR_PLATFORM,
UPDATE_ERR_TARGET,
+ UPDATE_ERR_ROOT_KEY,
UPDATE_ERR_TPM_ROLLBACK,
UPDATE_ERR_UNKNOWN,
};
@@ -1044,6 +1170,7 @@ static const char * const updater_error_messages[] = {
[UPDATE_ERR_WRITE_FIRMWARE] = "Failed writing firmware.",
[UPDATE_ERR_PLATFORM] = "Your system platform is not compatible.",
[UPDATE_ERR_TARGET] = "No valid RW target to update. Abort.",
+ [UPDATE_ERR_ROOT_KEY] = "RW not signed by same RO root key",
[UPDATE_ERR_TPM_ROLLBACK] = "RW not usable due to TPM anti-rollback.",
[UPDATE_ERR_UNKNOWN] = "Unknown error.",
};
@@ -1070,6 +1197,8 @@ static enum updater_error_codes update_try_rw_firmware(
return UPDATE_ERR_NEED_RO_UPDATE;
printf("Checking compatibility...\n");
+ if (check_compatible_root_key(image_from, image_to))
+ return UPDATE_ERR_ROOT_KEY;
if (check_compatible_tpm_keys(cfg, image_to))
return UPDATE_ERR_TPM_ROLLBACK;
@@ -1126,6 +1255,8 @@ static enum updater_error_codes update_rw_firmrware(
FMAP_RW_SECTION_A, FMAP_RW_SECTION_B, FMAP_RW_SHARED);
printf("Checking compatibility...\n");
+ if (check_compatible_root_key(image_from, image_to))
+ return UPDATE_ERR_ROOT_KEY;
if (check_compatible_tpm_keys(cfg, image_to))
return UPDATE_ERR_TPM_ROLLBACK;
/*
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 26d993d8..d51219ff 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -62,12 +62,18 @@ unpack_image() {
local image="$2"
mkdir -p "${folder}"
(cd "${folder}" && ${FUTILITY} dump_fmap -x "../${image}")
+ ${FUTILITY} gbb -g --rootkey="${folder}/rootkey" "${image}"
}
# Unpack images so we can prepare expected results by individual sections.
unpack_image "to" "${TO_IMAGE}"
unpack_image "from" "${FROM_IMAGE}"
+# Hack FROM_IMAGE so it has same root key as TO_IMAGE (for RW update).
+FROM_DIFFERENT_ROOTKEY_IMAGE="${FROM_IMAGE}2"
+cp -f "${FROM_IMAGE}" "${FROM_DIFFERENT_ROOTKEY_IMAGE}"
+"${FUTILITY}" gbb -s --rootkey="${TMP}.to/rootkey" "${FROM_IMAGE}"
+
# Generate expected results.
cp -f "${TO_IMAGE}" "${TMP}.expected.full"
cp -f "${FROM_IMAGE}" "${TMP}.expected.rw"
@@ -136,6 +142,10 @@ test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
-i "${LINK_BIOS}" --wp=1 --sys_props 0,0x10001,1
+test_update "RW update (incompatible rootkey)" \
+ "${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW not signed by same RO root key" \
+ -i "${TO_IMAGE}" --wp=1 --sys_props 0,0x10001,1
+
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected (2->1)" \
-i "${TO_IMAGE}" --wp=1 --sys_props 1,0x20001,1
@@ -160,6 +170,10 @@ test_update "RW update (incompatible platform)" \
"${FROM_IMAGE}" "!platform is not compatible" \
-i "${LINK_BIOS}" -t --wp=1 --sys_props 0x10001,1
+test_update "RW update (incompatible rootkey)" \
+ "${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW not signed by same RO root key" \
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 0,0x10001,1
+
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected (2->1)" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x20001,1