[iot] Update libavb in u-boot
This commit did:
1. Sync AVB lib with external/avb, head of commit is:
commit 6d5326a945c2d17d5d0e7718d5cb97663c3b33a2
Author: Neal Ostrem <nealo@google.com>
Date: Tue Apr 24 13:09:45 2018 -0700
Merge fix/changes required after merge from AOSP ToT.
Change library name to one used by AT.
Test: Built successfully and unit tests pass.
Change-Id: I5e5fc9a6010d96cfecfc6faf0858ba930cba65a0
2. Change product id in ATX to be full zeros to sync with
external/avb.
3. Fix build errors and implement ops fsl_set_key_version.
4. Move most nxp modified code to lib/avb/fsl/.
Test: build and boot successfully for imx7d_pico and imx8m_phanbell.
Change-Id: I199a035fe8267b10955299a4b745458d40a2e754
Signed-off-by: Luo Ji <ji.luo@nxp.com>
This commit is contained in:
parent
fed4a26ead
commit
449f9048e6
|
|
@ -5,6 +5,8 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
#
|
#
|
||||||
|
|
||||||
|
ccflags-y += -I./lib/avb
|
||||||
|
|
||||||
obj-$(CONFIG_USB_GADGET) += epautoconf.o config.o usbstring.o
|
obj-$(CONFIG_USB_GADGET) += epautoconf.o config.o usbstring.o
|
||||||
obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
|
obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1628,7 +1628,8 @@ static AvbABOps fsl_avb_ab_ops = {
|
||||||
static AvbAtxOps fsl_avb_atx_ops = {
|
static AvbAtxOps fsl_avb_atx_ops = {
|
||||||
.ops = NULL,
|
.ops = NULL,
|
||||||
.read_permanent_attributes = fsl_read_permanent_attributes,
|
.read_permanent_attributes = fsl_read_permanent_attributes,
|
||||||
.read_permanent_attributes_hash = fsl_read_permanent_attributes_hash
|
.read_permanent_attributes_hash = fsl_read_permanent_attributes_hash,
|
||||||
|
.set_key_version = fsl_set_key_version
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
static AvbOps fsl_avb_ops = {
|
static AvbOps fsl_avb_ops = {
|
||||||
|
|
|
||||||
|
|
@ -60,5 +60,6 @@
|
||||||
#ifdef CONFIG_FSL_CAAM_KB
|
#ifdef CONFIG_FSL_CAAM_KB
|
||||||
#undef CONFIG_FSL_CAAM_KB
|
#undef CONFIG_FSL_CAAM_KB
|
||||||
#endif
|
#endif
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8MM_EVK_ANDROID_H */
|
#endif /* IMX8MM_EVK_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -49,5 +49,6 @@
|
||||||
#ifdef CONFIG_FSL_CAAM_KB
|
#ifdef CONFIG_FSL_CAAM_KB
|
||||||
#undef CONFIG_FSL_CAAM_KB
|
#undef CONFIG_FSL_CAAM_KB
|
||||||
#endif
|
#endif
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8MQ_EVK_ANDROID_H */
|
#endif /* IMX8MQ_EVK_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -51,4 +51,6 @@
|
||||||
"fdt_high=0xffffffffffffffff\0" \
|
"fdt_high=0xffffffffffffffff\0" \
|
||||||
"initrd_high=0xffffffffffffffff\0" \
|
"initrd_high=0xffffffffffffffff\0" \
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8MQ_EVK_ANDROID_THINGS_H */
|
#endif /* IMX8MQ_EVK_ANDROID_THINGS_H */
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,6 @@
|
||||||
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
||||||
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* MX6_SABRE_ANDROID_COMMON_H */
|
#endif /* MX6_SABRE_ANDROID_COMMON_H */
|
||||||
|
|
|
||||||
|
|
@ -64,4 +64,6 @@
|
||||||
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
||||||
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QM_MEK_ANDROID_H */
|
#endif /* IMX8QM_MEK_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -93,4 +93,6 @@
|
||||||
#include "imx8qm_mek_android_auto_xen.h"
|
#include "imx8qm_mek_android_auto_xen.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QM_MEK_ANDROID_AUTO_H */
|
#endif /* IMX8QM_MEK_ANDROID_AUTO_H */
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,6 @@
|
||||||
/* This needs to be stay same in iomem in domu.cfg */
|
/* This needs to be stay same in iomem in domu.cfg */
|
||||||
#define SC_IPC_CH 0x15d1d0000
|
#define SC_IPC_CH 0x15d1d0000
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QM_MEK_ANDROID_AUTO_XEN_H */
|
#endif /* IMX8QM_MEK_ANDROID_AUTO_XEN_H */
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,6 @@
|
||||||
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
||||||
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QXP_ARM2_ANDROID_H */
|
#endif /* IMX8QXP_ARM2_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,6 @@
|
||||||
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
#define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR
|
||||||
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
#define CONFIG_FASTBOOT_BUF_SIZE 0x19000000
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QXP_MEK_ANDROID_H */
|
#endif /* IMX8QXP_MEK_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -87,4 +87,6 @@
|
||||||
#undef CONFIG_USB_HOST_ETHER
|
#undef CONFIG_USB_HOST_ETHER
|
||||||
#undef CONFIG_USB_FUNCTION_MASS_STORAGE
|
#undef CONFIG_USB_FUNCTION_MASS_STORAGE
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* IMX8QXP_MEK_ANDROID_AUTO_H */
|
#endif /* IMX8QXP_MEK_ANDROID_AUTO_H */
|
||||||
|
|
|
||||||
|
|
@ -61,4 +61,6 @@
|
||||||
#define CONFIG_AVB_FUSE_BANK_END 0
|
#define CONFIG_AVB_FUSE_BANK_END 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,6 @@
|
||||||
|
|
||||||
#endif /* CONFIG_AVB_SUPPORT */
|
#endif /* CONFIG_AVB_SUPPORT */
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* MX6_SABRE_ANDROID_COMMON_H */
|
#endif /* MX6_SABRE_ANDROID_COMMON_H */
|
||||||
|
|
|
||||||
|
|
@ -49,4 +49,6 @@
|
||||||
|
|
||||||
#endif /* CONFIG_AVB_SUPPORT */
|
#endif /* CONFIG_AVB_SUPPORT */
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,6 @@
|
||||||
|
|
||||||
#endif /* CONFIG_AVB_SUPPORT */
|
#endif /* CONFIG_AVB_SUPPORT */
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,6 @@
|
||||||
|
|
||||||
#endif /* CONFIG_AVB_SUPPORT */
|
#endif /* CONFIG_AVB_SUPPORT */
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -55,5 +55,7 @@
|
||||||
#define CONFIG_AVB_FUSE_BANK_END 15
|
#define CONFIG_AVB_FUSE_BANK_END 15
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* __MX6UL_NXPU_IOPB_ANDROID_THINGS_H */
|
/* __MX6UL_NXPU_IOPB_ANDROID_THINGS_H */
|
||||||
|
|
|
||||||
|
|
@ -48,5 +48,7 @@
|
||||||
#define CONFIG_AVB_FUSE_BANK_END 15
|
#define CONFIG_AVB_FUSE_BANK_END 15
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* __MX6UL_SPRIOT_ANDROID_THINGS_H */
|
/* __MX6UL_SPRIOT_ANDROID_THINGS_H */
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,6 @@
|
||||||
|
|
||||||
#endif /* CONFIG_AVB_SUPPORT */
|
#endif /* CONFIG_AVB_SUPPORT */
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif /* __MX7D_SABRESD_ANDROID_H */
|
#endif /* __MX7D_SABRESD_ANDROID_H */
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,6 @@
|
||||||
#define ANDROID_MCU_FIRMWARE_SIZE 0x20000
|
#define ANDROID_MCU_FIRMWARE_SIZE 0x20000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
|
|
||||||
#ifdef CONFIG_SYS_MALLOC_LEN
|
#ifdef CONFIG_SYS_MALLOC_LEN
|
||||||
#undef CONFIG_SYS_MALLOC_LEN
|
#undef CONFIG_SYS_MALLOC_LEN
|
||||||
#define CONFIG_SYS_MALLOC_LEN (32 * SZ_1M)
|
#define CONFIG_SYS_MALLOC_LEN (64 * SZ_1M)
|
||||||
#endif
|
#endif
|
||||||
/* fuse bank size in word */
|
/* fuse bank size in word */
|
||||||
/* infact 7D have no enough bits
|
/* infact 7D have no enough bits
|
||||||
|
|
@ -72,6 +72,8 @@
|
||||||
#define CONFIG_AVB_FUSE_BANK_END 14
|
#define CONFIG_AVB_FUSE_BANK_END 14
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
/* Disable U-Boot logo */
|
/* Disable U-Boot logo */
|
||||||
#undef CONFIG_VIDEO_LOGO
|
#undef CONFIG_VIDEO_LOGO
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
#define CONFIG_AVB_FUSE_BANK_END 15
|
#define CONFIG_AVB_FUSE_BANK_END 15
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* __PICOSOM_IMX6UL_ANDROID_THINGS_H */
|
/* __PICOSOM_IMX6UL_ANDROID_THINGS_H */
|
||||||
|
|
|
||||||
|
|
@ -195,4 +195,28 @@ AvbIOResult fsl_read_permanent_attributes(
|
||||||
*/
|
*/
|
||||||
AvbIOResult fsl_read_permanent_attributes_hash(
|
AvbIOResult fsl_read_permanent_attributes_hash(
|
||||||
AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
|
AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
|
||||||
|
|
||||||
|
/* Provides the key version of a key used during verification. This may be
|
||||||
|
* useful for managing the minimum key version.
|
||||||
|
*/
|
||||||
|
void fsl_set_key_version(AvbAtxOps* atx_ops,
|
||||||
|
size_t rollback_index_location,
|
||||||
|
uint64_t key_version);
|
||||||
|
|
||||||
|
/* This is the fast version of avb_ab_flow(), this function will
|
||||||
|
* not check another slot if one slot can pass the verify (or verify
|
||||||
|
* fail is acceptable). */
|
||||||
|
AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops,
|
||||||
|
const char* const* requested_partitions,
|
||||||
|
AvbSlotVerifyFlags flags,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode,
|
||||||
|
AvbSlotVerifyData** out_data);
|
||||||
|
|
||||||
|
/* This is for legacy i.mx6/7 which don't enable A/B but want to
|
||||||
|
* verify boot/recovery with AVB */
|
||||||
|
AvbABFlowResult avb_single_flow(AvbABOps* ab_ops,
|
||||||
|
const char* const* requested_partitions,
|
||||||
|
AvbSlotVerifyFlags flags,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode,
|
||||||
|
AvbSlotVerifyData** out_data);
|
||||||
#endif /* __FSL_AVB_H__ */
|
#endif /* __FSL_AVB_H__ */
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
subdir-ccflags-y += -D_FILE_OFFSET_BITS=64 \
|
subdir-ccflags-y += -I./lib/avb \
|
||||||
|
-D_FILE_OFFSET_BITS=64 \
|
||||||
-D_POSIX_C_SOURCE=199309L \
|
-D_POSIX_C_SOURCE=199309L \
|
||||||
-Wa,--noexecstack \
|
-Wa,--noexecstack \
|
||||||
-Werror \
|
|
||||||
-Wall \
|
-Wall \
|
||||||
-Wextra \
|
-Wextra \
|
||||||
-Wformat=2 \
|
-Wformat=2 \
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
ccflags-$(CONFIG_AVB_DEBUG) += -DAVB_DEBUG
|
ccflags-y += -Werror
|
||||||
obj-y += fsl_avb.o
|
obj-y += fsl_avb.o
|
||||||
obj-y += fsl_avbkey.o
|
obj-y += fsl_avbkey.o
|
||||||
obj-y += fsl_bootctl.o
|
obj-y += fsl_bootctl.o
|
||||||
|
obj-y += fsl_avb_ab_flow.o
|
||||||
|
obj-y += fsl_avb_sysdeps_uboot.o
|
||||||
obj-y += utils.o
|
obj-y += utils.o
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
/* This product_id is generated from
|
/* This product_id is generated from
|
||||||
* extern/avb/test/data/atx_product_id.bin */
|
* extern/avb/test/data/atx_product_id.bin */
|
||||||
unsigned char fsl_atx_product_id[] = {
|
unsigned char fsl_atx_product_id[] = {
|
||||||
0x3f,0x38,0x9c,0xcb,0xbe,0x56,0xcc,0x3d,
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
0x0b,0xd0,0xbb,0x35,0x01,0x85,0xa7,0xd2
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||||
};
|
};
|
||||||
/* This product_root_public_key is generated form
|
/* This product_root_public_key is generated form
|
||||||
* extern/avb/test/data/testkey_atx_prk.pem */
|
* extern/avb/test/data/testkey_atx_prk.pem */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,398 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 NXP
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <fsl_avb.h>
|
||||||
|
|
||||||
|
static const char* slot_suffixes[2] = {"_a", "_b"};
|
||||||
|
|
||||||
|
/* This is a copy of slot_set_unbootable() form
|
||||||
|
* lib/avb/libavb_ab/avb_ab_flow.c.
|
||||||
|
*/
|
||||||
|
static void fsl_slot_set_unbootable(AvbABSlotData* slot) {
|
||||||
|
slot->priority = 0;
|
||||||
|
slot->tries_remaining = 0;
|
||||||
|
slot->successful_boot = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure all unbootable and/or illegal states are marked as the
|
||||||
|
* canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
|
||||||
|
* and successful_boot=0. This is a copy of fsl_slot_normalize from
|
||||||
|
* lib/avb/libavb_ab/avb_ab_flow.c.
|
||||||
|
*/
|
||||||
|
static void fsl_slot_normalize(AvbABSlotData* slot) {
|
||||||
|
if (slot->priority > 0) {
|
||||||
|
if ((slot->tries_remaining == 0) && (!slot->successful_boot)) {
|
||||||
|
/* We've exhausted all tries -> unbootable. */
|
||||||
|
fsl_slot_set_unbootable(slot);
|
||||||
|
}
|
||||||
|
if ((slot->tries_remaining > 0) && (slot->successful_boot)) {
|
||||||
|
/* Illegal state - avb_ab_mark_slot_successful() will clear
|
||||||
|
* tries_remaining when setting successful_boot.
|
||||||
|
*/
|
||||||
|
fsl_slot_set_unbootable(slot);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fsl_slot_set_unbootable(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes A/B metadata to disk only if it has changed - returns
|
||||||
|
* AVB_IO_RESULT_OK on success, error code otherwise. This is a
|
||||||
|
* copy of save_metadata_if_changed form lib/avb/libavb_ab/avb_ab_flow.c.
|
||||||
|
*/
|
||||||
|
static AvbIOResult fsl_save_metadata_if_changed(AvbABOps* ab_ops,
|
||||||
|
AvbABData* ab_data,
|
||||||
|
AvbABData* ab_data_orig) {
|
||||||
|
if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) {
|
||||||
|
avb_debug("Writing A/B metadata to disk.\n");
|
||||||
|
return ab_ops->write_ab_metadata(ab_ops, ab_data);
|
||||||
|
}
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is a copy of slot_is_bootable() from
|
||||||
|
* lib/avb/libavb_ab/avb_ab_flow.c.
|
||||||
|
*/
|
||||||
|
static bool fsl_slot_is_bootable(AvbABSlotData* slot) {
|
||||||
|
return (slot->priority > 0) &&
|
||||||
|
(slot->successful_boot || (slot->tries_remaining > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function to load metadata - returns AVB_IO_RESULT_OK on
|
||||||
|
* success, error code otherwise. This is a copy of load_metadata()
|
||||||
|
* from /lib/avb/libavb_ab/avb_ab_flow.c.
|
||||||
|
*/
|
||||||
|
static AvbIOResult fsl_load_metadata(AvbABOps* ab_ops,
|
||||||
|
AvbABData* ab_data,
|
||||||
|
AvbABData* ab_data_orig) {
|
||||||
|
AvbIOResult io_ret;
|
||||||
|
|
||||||
|
io_ret = ab_ops->read_ab_metadata(ab_ops, ab_data);
|
||||||
|
if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("I/O error while loading A/B metadata.\n");
|
||||||
|
return io_ret;
|
||||||
|
}
|
||||||
|
*ab_data_orig = *ab_data;
|
||||||
|
|
||||||
|
/* Ensure data is normalized, e.g. illegal states will be marked as
|
||||||
|
* unbootable and all unbootable states are represented with
|
||||||
|
* (priority=0, tries_remaining=0, successful_boot=0).
|
||||||
|
*/
|
||||||
|
fsl_slot_normalize(&ab_data->slots[0]);
|
||||||
|
fsl_slot_normalize(&ab_data->slots[1]);
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For legacy i.mx6/7, we won't enable A/B due to the limitation of
|
||||||
|
* storage capacity, but we still want to verify boot/recovery with
|
||||||
|
* AVB. */
|
||||||
|
AvbABFlowResult avb_single_flow(AvbABOps* ab_ops,
|
||||||
|
const char* const* requested_partitions,
|
||||||
|
AvbSlotVerifyFlags flags,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode,
|
||||||
|
AvbSlotVerifyData** out_data) {
|
||||||
|
AvbOps* ops = ab_ops->ops;
|
||||||
|
AvbSlotVerifyData* slot_data = NULL;
|
||||||
|
AvbSlotVerifyData* data = NULL;
|
||||||
|
AvbABFlowResult ret;
|
||||||
|
bool saw_and_allowed_verification_error = false;
|
||||||
|
|
||||||
|
/* Validate boot/recovery. */
|
||||||
|
AvbSlotVerifyResult verify_result;
|
||||||
|
|
||||||
|
verify_result = avb_slot_verify(ops,
|
||||||
|
requested_partitions,
|
||||||
|
"",
|
||||||
|
flags,
|
||||||
|
hashtree_error_mode,
|
||||||
|
&slot_data);
|
||||||
|
switch (verify_result) {
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
|
||||||
|
/* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
||||||
|
* these mean game over.
|
||||||
|
*/
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* explicit fallthrough. */
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
|
||||||
|
if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
|
||||||
|
/* Do nothing since we allow this. */
|
||||||
|
avb_debugv("Allowing slot ",
|
||||||
|
slot_suffixes[n],
|
||||||
|
" which verified "
|
||||||
|
"with result ",
|
||||||
|
avb_slot_verify_result_to_string(verify_result),
|
||||||
|
" because "
|
||||||
|
"AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
|
||||||
|
"is set.\n",
|
||||||
|
NULL);
|
||||||
|
saw_and_allowed_verification_error = true;
|
||||||
|
} else {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
|
||||||
|
goto out;
|
||||||
|
/* Do not add a 'default:' case here because of -Wswitch. */
|
||||||
|
}
|
||||||
|
|
||||||
|
avb_assert(slot_data != NULL);
|
||||||
|
data = slot_data;
|
||||||
|
slot_data = NULL;
|
||||||
|
if (saw_and_allowed_verification_error) {
|
||||||
|
avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
||||||
|
ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
|
||||||
|
} else {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (slot_data != NULL) {
|
||||||
|
avb_slot_verify_data_free(slot_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_data != NULL) {
|
||||||
|
*out_data = data;
|
||||||
|
} else {
|
||||||
|
if (data != NULL) {
|
||||||
|
avb_slot_verify_data_free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops,
|
||||||
|
const char* const* requested_partitions,
|
||||||
|
AvbSlotVerifyFlags flags,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode,
|
||||||
|
AvbSlotVerifyData** out_data) {
|
||||||
|
AvbOps* ops = ab_ops->ops;
|
||||||
|
AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
|
||||||
|
AvbSlotVerifyData* data = NULL;
|
||||||
|
AvbABFlowResult ret;
|
||||||
|
AvbABData ab_data, ab_data_orig;
|
||||||
|
size_t slot_index_to_boot, n;
|
||||||
|
AvbIOResult io_ret;
|
||||||
|
bool saw_and_allowed_verification_error = false;
|
||||||
|
size_t target_slot;
|
||||||
|
AvbSlotVerifyResult verify_result;
|
||||||
|
bool set_slot_unbootable = false;
|
||||||
|
|
||||||
|
io_ret = fsl_load_metadata(ab_ops, &ab_data, &ab_data_orig);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_index_to_boot = 2; // Means not 0 or 1
|
||||||
|
target_slot =
|
||||||
|
(ab_data.slots[1].priority > ab_data.slots[0].priority) ? 1 : 0;
|
||||||
|
|
||||||
|
for (n = 0; n < 2; n++) {
|
||||||
|
if (!fsl_slot_is_bootable(&ab_data.slots[target_slot])) {
|
||||||
|
target_slot = (target_slot == 1 ? 0 : 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
verify_result = avb_slot_verify(ops,
|
||||||
|
requested_partitions,
|
||||||
|
slot_suffixes[target_slot],
|
||||||
|
flags,
|
||||||
|
hashtree_error_mode,
|
||||||
|
&slot_data[target_slot]);
|
||||||
|
switch (verify_result) {
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_OK:
|
||||||
|
slot_index_to_boot = target_slot;
|
||||||
|
n = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
|
||||||
|
/* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
||||||
|
* these mean game over.
|
||||||
|
*/
|
||||||
|
set_slot_unbootable = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* explicit fallthrough. */
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
|
||||||
|
if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
|
||||||
|
/* Do nothing since we allow this. */
|
||||||
|
avb_debugv("Allowing slot ",
|
||||||
|
slot_suffixes[target_slot],
|
||||||
|
" which verified "
|
||||||
|
"with result ",
|
||||||
|
avb_slot_verify_result_to_string(verify_result),
|
||||||
|
" because "
|
||||||
|
"AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
|
||||||
|
"is set.\n",
|
||||||
|
NULL);
|
||||||
|
saw_and_allowed_verification_error =
|
||||||
|
true;
|
||||||
|
slot_index_to_boot = target_slot;
|
||||||
|
n = 2;
|
||||||
|
} else {
|
||||||
|
set_slot_unbootable = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
|
||||||
|
goto out;
|
||||||
|
/* Do not add a 'default:' case here because
|
||||||
|
* of -Wswitch.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_slot_unbootable) {
|
||||||
|
avb_errorv("Error verifying slot ",
|
||||||
|
slot_suffixes[target_slot],
|
||||||
|
" with result ",
|
||||||
|
avb_slot_verify_result_to_string(verify_result),
|
||||||
|
" - setting unbootable.\n",
|
||||||
|
NULL);
|
||||||
|
fsl_slot_set_unbootable(&ab_data.slots[target_slot]);
|
||||||
|
set_slot_unbootable = false;
|
||||||
|
}
|
||||||
|
/* switch to another slot */
|
||||||
|
target_slot = (target_slot == 1 ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot_index_to_boot == 2) {
|
||||||
|
/* No bootable slots! */
|
||||||
|
avb_error("No bootable slots found.\n");
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update stored rollback index such that the stored rollback index
|
||||||
|
* is the largest value supporting all currently bootable slots. Do
|
||||||
|
* this for every rollback index location.
|
||||||
|
*/
|
||||||
|
for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
|
||||||
|
uint64_t rollback_index_value = 0;
|
||||||
|
|
||||||
|
if ((slot_data[0] != NULL) && (slot_data[1] != NULL)) {
|
||||||
|
uint64_t a_rollback_index =
|
||||||
|
slot_data[0]->rollback_indexes[n];
|
||||||
|
uint64_t b_rollback_index =
|
||||||
|
slot_data[1]->rollback_indexes[n];
|
||||||
|
rollback_index_value =
|
||||||
|
(a_rollback_index < b_rollback_index ?
|
||||||
|
a_rollback_index : b_rollback_index);
|
||||||
|
} else if (slot_data[0] != NULL) {
|
||||||
|
rollback_index_value =
|
||||||
|
slot_data[0]->rollback_indexes[n];
|
||||||
|
} else if (slot_data[1] != NULL) {
|
||||||
|
rollback_index_value =
|
||||||
|
slot_data[1]->rollback_indexes[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rollback_index_value != 0) {
|
||||||
|
uint64_t current_rollback_index_value;
|
||||||
|
io_ret = ops->read_rollback_index(
|
||||||
|
ops, n, ¤t_rollback_index_value);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Error getting rollback index for slot.\n");
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (current_rollback_index_value != rollback_index_value) {
|
||||||
|
io_ret = ops->write_rollback_index(
|
||||||
|
ops, n, rollback_index_value);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Error setting stored rollback index.\n");
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, select this slot. */
|
||||||
|
avb_assert(slot_data[slot_index_to_boot] != NULL);
|
||||||
|
data = slot_data[slot_index_to_boot];
|
||||||
|
slot_data[slot_index_to_boot] = NULL;
|
||||||
|
if (saw_and_allowed_verification_error) {
|
||||||
|
avb_assert(
|
||||||
|
flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
||||||
|
ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
|
||||||
|
} else {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... and decrement tries remaining, if applicable. */
|
||||||
|
if (!ab_data.slots[slot_index_to_boot].successful_boot &&
|
||||||
|
(ab_data.slots[slot_index_to_boot].tries_remaining > 0)) {
|
||||||
|
ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
io_ret = fsl_save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
|
||||||
|
if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
||||||
|
} else {
|
||||||
|
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
if (data != NULL) {
|
||||||
|
avb_slot_verify_data_free(data);
|
||||||
|
data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < 2; n++) {
|
||||||
|
if (slot_data[n] != NULL) {
|
||||||
|
avb_slot_verify_data_free(slot_data[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_data != NULL) {
|
||||||
|
*out_data = data;
|
||||||
|
} else {
|
||||||
|
if (data != NULL) {
|
||||||
|
avb_slot_verify_data_free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
#include "avb_sysdeps.h"
|
#include "../libavb/libavb.h"
|
||||||
|
|
||||||
int avb_memcmp(const void* src1, const void* src2, size_t n) {
|
int avb_memcmp(const void* src1, const void* src2, size_t n) {
|
||||||
return memcmp(src1, src2, n);
|
return memcmp(src1, src2, n);
|
||||||
|
|
@ -62,3 +62,9 @@ void avb_printv(const char* message, ...) {
|
||||||
void* avb_malloc_(size_t size) { return malloc(size); }
|
void* avb_malloc_(size_t size) { return malloc(size); }
|
||||||
|
|
||||||
void avb_free(void* ptr) { free(ptr); }
|
void avb_free(void* ptr) { free(ptr); }
|
||||||
|
|
||||||
|
uint32_t avb_div_by_10(uint64_t* dividend) {
|
||||||
|
uint32_t rem = (uint32_t)(*dividend % 10);
|
||||||
|
*dividend /= 10;
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
|
|
@ -1108,7 +1108,8 @@ AvbIOResult fsl_write_rollback_index_rpmb(AvbOps* ops, size_t rollback_index_slo
|
||||||
*plain_idx = rollback_index;
|
*plain_idx = rollback_index;
|
||||||
|
|
||||||
/* write rollback_index keyblob */
|
/* write rollback_index keyblob */
|
||||||
if (rpmb_write(mmc_dev, (uint8_t *)plain_idx, rbk->len, rbk->offset) != 0) {
|
if (rpmb_write(mmc_dev, (uint8_t *)plain_idx, rbk->len, rbk->offset) !=
|
||||||
|
0) {
|
||||||
ERR("write rollback index error\n");
|
ERR("write rollback index error\n");
|
||||||
ret = AVB_IO_RESULT_ERROR_IO;
|
ret = AVB_IO_RESULT_ERROR_IO;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1131,8 +1132,9 @@ AvbIOResult fsl_read_permanent_attributes(
|
||||||
/* use hard code permanent attributes due to limited fuse and RPMB */
|
/* use hard code permanent attributes due to limited fuse and RPMB */
|
||||||
attributes->version = fsl_version;
|
attributes->version = fsl_version;
|
||||||
memcpy(attributes->product_root_public_key, fsl_product_root_public_key,
|
memcpy(attributes->product_root_public_key, fsl_product_root_public_key,
|
||||||
sizeof(fsl_product_root_public_key));
|
sizeof(fsl_product_root_public_key));
|
||||||
memcpy(attributes->product_id, fsl_atx_product_id, sizeof(fsl_atx_product_id));
|
memcpy(attributes->product_id, fsl_atx_product_id,
|
||||||
|
sizeof(fsl_atx_product_id));
|
||||||
|
|
||||||
return AVB_IO_RESULT_OK;
|
return AVB_IO_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -1148,8 +1150,9 @@ AvbIOResult fsl_read_permanent_attributes_hash(
|
||||||
|
|
||||||
/* read first 112 bits of sha256(permanent attributes) from fuse */
|
/* read first 112 bits of sha256(permanent attributes) from fuse */
|
||||||
if (fsl_fuse_read(sha256_hash_fuse, ATX_FUSE_BANK_NUM,
|
if (fsl_fuse_read(sha256_hash_fuse, ATX_FUSE_BANK_NUM,
|
||||||
PERMANENT_ATTRIBUTE_HASH_OFFSET)) {
|
PERMANENT_ATTRIBUTE_HASH_OFFSET)) {
|
||||||
printf("ERROR - read permanent attributes hash from fuse error\n");
|
printf("ERROR - read permanent attributes hash from "
|
||||||
|
"fuse error\n");
|
||||||
return AVB_IO_RESULT_ERROR_IO;
|
return AVB_IO_RESULT_ERROR_IO;
|
||||||
}
|
}
|
||||||
/* only take the lower 2 bytes of last bank */
|
/* only take the lower 2 bytes of last bank */
|
||||||
|
|
@ -1168,4 +1171,54 @@ AvbIOResult fsl_read_permanent_attributes_hash(
|
||||||
memcpy(hash, sha256_hash_buf, AVB_SHA256_DIGEST_SIZE);
|
memcpy(hash, sha256_hash_buf, AVB_SHA256_DIGEST_SIZE);
|
||||||
return AVB_IO_RESULT_OK;
|
return AVB_IO_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Provides the key version of a key used during verification. This may be
|
||||||
|
* useful for managing the minimum key version.
|
||||||
|
*/
|
||||||
|
void fsl_set_key_version(AvbAtxOps* atx_ops,
|
||||||
|
size_t rollback_index_location,
|
||||||
|
uint64_t key_version) {
|
||||||
|
kblb_hdr_t hdr;
|
||||||
|
kblb_tag_t *rbk;
|
||||||
|
uint64_t *plain_idx = NULL;
|
||||||
|
struct mmc *mmc_dev;
|
||||||
|
static const uint32_t kTypeMask = 0xF000;
|
||||||
|
|
||||||
|
DEBUGAVB("[rpmb] write to rollback slot: (%zu, %" PRIu64 ")\n",
|
||||||
|
rollback_index_location, key_version);
|
||||||
|
|
||||||
|
assert(atx_ops != NULL);
|
||||||
|
|
||||||
|
if ((mmc_dev = get_mmc()) == NULL) {
|
||||||
|
ERR("err get mmc device\n");
|
||||||
|
}
|
||||||
|
/* read the kblb header */
|
||||||
|
if (rpmb_read(mmc_dev, (uint8_t *)&hdr, sizeof(hdr), 0) != 0) {
|
||||||
|
ERR("read RPMB error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(hdr.magic, AVB_KBLB_MAGIC, AVB_KBLB_MAGIC_LEN) != 0) {
|
||||||
|
ERR("magic not match\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rollback index for Android Things key versions */
|
||||||
|
rbk = &hdr.atx_rbk_tags[rollback_index_location & ~kTypeMask];
|
||||||
|
|
||||||
|
plain_idx = malloc(rbk->len);
|
||||||
|
if (plain_idx == NULL)
|
||||||
|
printf("\nError! allocate memory fail!\n");
|
||||||
|
memset(plain_idx, 0, rbk->len);
|
||||||
|
*plain_idx = key_version;
|
||||||
|
|
||||||
|
/* write rollback_index keyblob */
|
||||||
|
if (rpmb_write(mmc_dev, (uint8_t *)plain_idx, rbk->len, rbk->offset) !=
|
||||||
|
0) {
|
||||||
|
ERR("write rollback index error\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
if (plain_idx != NULL)
|
||||||
|
free(plain_idx);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@ obj-y += avb_descriptor.o \
|
||||||
avb_hashtree_descriptor.o \
|
avb_hashtree_descriptor.o \
|
||||||
avb_sha256.o \
|
avb_sha256.o \
|
||||||
avb_util.o \
|
avb_util.o \
|
||||||
avb_sysdeps_uboot.o
|
avb_cmdline.o
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use, copy,
|
||||||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
* of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "avb_cmdline.h"
|
||||||
|
#include "avb_sha.h"
|
||||||
|
#include "avb_util.h"
|
||||||
|
#include "avb_version.h"
|
||||||
|
|
||||||
|
#define NUM_GUIDS 3
|
||||||
|
|
||||||
|
/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
|
||||||
|
* values. Returns NULL on OOM, otherwise the cmdline with values
|
||||||
|
* replaced.
|
||||||
|
*/
|
||||||
|
char* avb_sub_cmdline(AvbOps* ops,
|
||||||
|
const char* cmdline,
|
||||||
|
const char* ab_suffix,
|
||||||
|
bool using_boot_for_vbmeta,
|
||||||
|
const AvbCmdlineSubstList* additional_substitutions) {
|
||||||
|
const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
|
||||||
|
const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
|
||||||
|
"$(ANDROID_BOOT_PARTUUID)",
|
||||||
|
"$(ANDROID_VBMETA_PARTUUID)"};
|
||||||
|
char* ret = NULL;
|
||||||
|
AvbIOResult io_ret;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
/* Special-case for when the top-level vbmeta struct is in the boot
|
||||||
|
* partition.
|
||||||
|
*/
|
||||||
|
if (using_boot_for_vbmeta) {
|
||||||
|
part_name_str[2] = "boot";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace unique partition GUIDs */
|
||||||
|
for (n = 0; n < NUM_GUIDS; n++) {
|
||||||
|
char part_name[AVB_PART_NAME_MAX_SIZE];
|
||||||
|
char guid_buf[37];
|
||||||
|
|
||||||
|
if (!avb_str_concat(part_name,
|
||||||
|
sizeof part_name,
|
||||||
|
part_name_str[n],
|
||||||
|
avb_strlen(part_name_str[n]),
|
||||||
|
ab_suffix,
|
||||||
|
avb_strlen(ab_suffix))) {
|
||||||
|
avb_error("Partition name and suffix does not fit.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_ret = ops->get_unique_guid_for_partition(
|
||||||
|
ops, part_name, guid_buf, sizeof guid_buf);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
goto fail;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Error getting unique GUID for partition.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == NULL) {
|
||||||
|
ret = avb_replace(cmdline, replace_str[n], guid_buf);
|
||||||
|
} else {
|
||||||
|
char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
|
||||||
|
avb_free(ret);
|
||||||
|
ret = new_ret;
|
||||||
|
}
|
||||||
|
if (ret == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avb_assert(ret != NULL);
|
||||||
|
|
||||||
|
/* Replace any additional substitutions. */
|
||||||
|
if (additional_substitutions != NULL) {
|
||||||
|
for (n = 0; n < additional_substitutions->size; ++n) {
|
||||||
|
char* new_ret = avb_replace(ret,
|
||||||
|
additional_substitutions->tokens[n],
|
||||||
|
additional_substitutions->values[n]);
|
||||||
|
avb_free(ret);
|
||||||
|
ret = new_ret;
|
||||||
|
if (ret == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (ret != NULL) {
|
||||||
|
avb_free(ret);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmdline_append_option(AvbSlotVerifyData* slot_data,
|
||||||
|
const char* key,
|
||||||
|
const char* value) {
|
||||||
|
size_t offset, key_len, value_len;
|
||||||
|
char* new_cmdline;
|
||||||
|
|
||||||
|
key_len = avb_strlen(key);
|
||||||
|
value_len = avb_strlen(value);
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
if (slot_data->cmdline != NULL) {
|
||||||
|
offset = avb_strlen(slot_data->cmdline);
|
||||||
|
if (offset > 0) {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_cmdline = avb_calloc(offset + key_len + value_len + 2);
|
||||||
|
if (new_cmdline == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (offset > 0) {
|
||||||
|
avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
|
||||||
|
new_cmdline[offset - 1] = ' ';
|
||||||
|
}
|
||||||
|
avb_memcpy(new_cmdline + offset, key, key_len);
|
||||||
|
new_cmdline[offset + key_len] = '=';
|
||||||
|
avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
|
||||||
|
if (slot_data->cmdline != NULL) {
|
||||||
|
avb_free(slot_data->cmdline);
|
||||||
|
}
|
||||||
|
slot_data->cmdline = new_cmdline;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AVB_MAX_DIGITS_UINT64 32
|
||||||
|
|
||||||
|
/* Writes |value| to |digits| in base 10 followed by a NUL byte.
|
||||||
|
* Returns number of characters written excluding the NUL byte.
|
||||||
|
*/
|
||||||
|
static size_t uint64_to_base10(uint64_t value,
|
||||||
|
char digits[AVB_MAX_DIGITS_UINT64]) {
|
||||||
|
char rev_digits[AVB_MAX_DIGITS_UINT64];
|
||||||
|
size_t n, num_digits;
|
||||||
|
|
||||||
|
for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
|
||||||
|
rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
|
||||||
|
if (value == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < num_digits; n++) {
|
||||||
|
digits[n] = rev_digits[num_digits - 1 - n];
|
||||||
|
}
|
||||||
|
digits[n] = '\0';
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmdline_append_version(AvbSlotVerifyData* slot_data,
|
||||||
|
const char* key,
|
||||||
|
uint64_t major_version,
|
||||||
|
uint64_t minor_version) {
|
||||||
|
char major_digits[AVB_MAX_DIGITS_UINT64];
|
||||||
|
char minor_digits[AVB_MAX_DIGITS_UINT64];
|
||||||
|
char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
|
||||||
|
size_t num_major_digits, num_minor_digits;
|
||||||
|
|
||||||
|
num_major_digits = uint64_to_base10(major_version, major_digits);
|
||||||
|
num_minor_digits = uint64_to_base10(minor_version, minor_digits);
|
||||||
|
avb_memcpy(combined, major_digits, num_major_digits);
|
||||||
|
combined[num_major_digits] = '.';
|
||||||
|
avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
|
||||||
|
combined[num_major_digits + 1 + num_minor_digits] = '\0';
|
||||||
|
|
||||||
|
return cmdline_append_option(slot_data, key, combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
|
||||||
|
const char* key,
|
||||||
|
uint64_t value) {
|
||||||
|
char digits[AVB_MAX_DIGITS_UINT64];
|
||||||
|
uint64_to_base10(value, digits);
|
||||||
|
return cmdline_append_option(slot_data, key, digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
|
||||||
|
const char* key,
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t data_len) {
|
||||||
|
int ret;
|
||||||
|
char* hex_data = avb_bin2hex(data, data_len);
|
||||||
|
if (hex_data == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ret = cmdline_append_option(slot_data, key, hex_data);
|
||||||
|
avb_free(hex_data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbSlotVerifyResult avb_append_options(
|
||||||
|
AvbOps* ops,
|
||||||
|
AvbSlotVerifyData* slot_data,
|
||||||
|
AvbVBMetaImageHeader* toplevel_vbmeta,
|
||||||
|
AvbAlgorithmType algorithm_type,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode) {
|
||||||
|
AvbSlotVerifyResult ret;
|
||||||
|
const char* verity_mode;
|
||||||
|
bool is_device_unlocked;
|
||||||
|
AvbIOResult io_ret;
|
||||||
|
|
||||||
|
/* Add androidboot.vbmeta.device option. */
|
||||||
|
if (!cmdline_append_option(slot_data,
|
||||||
|
"androidboot.vbmeta.device",
|
||||||
|
"PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add androidboot.vbmeta.avb_version option. */
|
||||||
|
if (!cmdline_append_version(slot_data,
|
||||||
|
"androidboot.vbmeta.avb_version",
|
||||||
|
AVB_VERSION_MAJOR,
|
||||||
|
AVB_VERSION_MINOR)) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set androidboot.avb.device_state to "locked" or "unlocked". */
|
||||||
|
io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Error getting device state.\n");
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!cmdline_append_option(slot_data,
|
||||||
|
"androidboot.vbmeta.device_state",
|
||||||
|
is_device_unlocked ? "unlocked" : "locked")) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
|
||||||
|
* function as is used to sign vbmeta.
|
||||||
|
*/
|
||||||
|
switch (algorithm_type) {
|
||||||
|
/* Explicit fallthrough. */
|
||||||
|
case AVB_ALGORITHM_TYPE_NONE:
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
|
||||||
|
size_t n, total_size = 0;
|
||||||
|
uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
avb_slot_verify_data_calculate_vbmeta_digest(
|
||||||
|
slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
|
||||||
|
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
|
||||||
|
total_size += slot_data->vbmeta_images[n].vbmeta_size;
|
||||||
|
}
|
||||||
|
if (!cmdline_append_option(
|
||||||
|
slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
|
||||||
|
!cmdline_append_uint64_base10(
|
||||||
|
slot_data, "androidboot.vbmeta.size", total_size) ||
|
||||||
|
!cmdline_append_hex(slot_data,
|
||||||
|
"androidboot.vbmeta.digest",
|
||||||
|
vbmeta_digest,
|
||||||
|
AVB_SHA256_DIGEST_SIZE)) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
/* Explicit fallthrough. */
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
|
||||||
|
case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
|
||||||
|
size_t n, total_size = 0;
|
||||||
|
uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
|
||||||
|
avb_slot_verify_data_calculate_vbmeta_digest(
|
||||||
|
slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
|
||||||
|
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
|
||||||
|
total_size += slot_data->vbmeta_images[n].vbmeta_size;
|
||||||
|
}
|
||||||
|
if (!cmdline_append_option(
|
||||||
|
slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
|
||||||
|
!cmdline_append_uint64_base10(
|
||||||
|
slot_data, "androidboot.vbmeta.size", total_size) ||
|
||||||
|
!cmdline_append_hex(slot_data,
|
||||||
|
"androidboot.vbmeta.digest",
|
||||||
|
vbmeta_digest,
|
||||||
|
AVB_SHA512_DIGEST_SIZE)) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case _AVB_ALGORITHM_NUM_TYPES:
|
||||||
|
avb_assert_not_reached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
|
||||||
|
if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
|
||||||
|
verity_mode = "disabled";
|
||||||
|
} else {
|
||||||
|
const char* dm_verity_mode;
|
||||||
|
char* new_ret;
|
||||||
|
|
||||||
|
switch (hashtree_error_mode) {
|
||||||
|
case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
|
||||||
|
if (!cmdline_append_option(
|
||||||
|
slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
verity_mode = "enforcing";
|
||||||
|
dm_verity_mode = "restart_on_corruption";
|
||||||
|
break;
|
||||||
|
case AVB_HASHTREE_ERROR_MODE_RESTART:
|
||||||
|
verity_mode = "enforcing";
|
||||||
|
dm_verity_mode = "restart_on_corruption";
|
||||||
|
break;
|
||||||
|
case AVB_HASHTREE_ERROR_MODE_EIO:
|
||||||
|
verity_mode = "eio";
|
||||||
|
/* For now there's no option to specify the EIO mode. So
|
||||||
|
* just use 'ignore_zero_blocks' since that's already set
|
||||||
|
* and dm-verity-target.c supports specifying this multiple
|
||||||
|
* times.
|
||||||
|
*/
|
||||||
|
dm_verity_mode = "ignore_zero_blocks";
|
||||||
|
break;
|
||||||
|
case AVB_HASHTREE_ERROR_MODE_LOGGING:
|
||||||
|
verity_mode = "logging";
|
||||||
|
dm_verity_mode = "ignore_corruption";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
new_ret = avb_replace(
|
||||||
|
slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
|
||||||
|
avb_free(slot_data->cmdline);
|
||||||
|
slot_data->cmdline = new_ret;
|
||||||
|
if (slot_data->cmdline == NULL) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cmdline_append_option(
|
||||||
|
slot_data, "androidboot.veritymode", verity_mode)) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_OK;
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
|
||||||
|
return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
|
||||||
|
}
|
||||||
|
|
||||||
|
void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < cmdline_subst->size; ++i) {
|
||||||
|
avb_free(cmdline_subst->tokens[i]);
|
||||||
|
avb_free(cmdline_subst->values[i]);
|
||||||
|
}
|
||||||
|
cmdline_subst->size = 0;
|
||||||
|
avb_free(cmdline_subst);
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbSlotVerifyResult avb_add_root_digest_substitution(
|
||||||
|
const char* part_name,
|
||||||
|
const uint8_t* digest,
|
||||||
|
size_t digest_size,
|
||||||
|
AvbCmdlineSubstList* out_cmdline_subst) {
|
||||||
|
const char* kDigestSubPrefix = "$(AVB_";
|
||||||
|
const char* kDigestSubSuffix = "_ROOT_DIGEST)";
|
||||||
|
size_t part_name_len = avb_strlen(part_name);
|
||||||
|
size_t list_index = out_cmdline_subst->size;
|
||||||
|
|
||||||
|
avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
|
||||||
|
avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
|
||||||
|
if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
|
||||||
|
digest_size > AVB_SHA512_DIGEST_SIZE) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
|
||||||
|
/* The list is full. Currently dynamic growth of this list is not supported.
|
||||||
|
*/
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Construct the token to replace in the command line based on the partition
|
||||||
|
* name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
|
||||||
|
*/
|
||||||
|
out_cmdline_subst->tokens[list_index] =
|
||||||
|
avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
|
||||||
|
if (out_cmdline_subst->tokens[list_index] == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
avb_uppercase(out_cmdline_subst->tokens[list_index]);
|
||||||
|
|
||||||
|
/* The digest value is hex encoded when inserted in the command line. */
|
||||||
|
out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
|
||||||
|
if (out_cmdline_subst->values[list_index] == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_cmdline_subst->size++;
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (out_cmdline_subst->tokens[list_index]) {
|
||||||
|
avb_free(out_cmdline_subst->tokens[list_index]);
|
||||||
|
}
|
||||||
|
if (out_cmdline_subst->values[list_index]) {
|
||||||
|
avb_free(out_cmdline_subst->values[list_index]);
|
||||||
|
}
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use, copy,
|
||||||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
* of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef AVB_INSIDE_LIBAVB_H
|
||||||
|
#error "You can't include avb_sha.h in the public header libavb.h."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AVB_COMPILATION
|
||||||
|
#error "Never include this file, it may only be used from internal avb code."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AVB_CMDLINE_H_
|
||||||
|
#define AVB_CMDLINE_H_
|
||||||
|
|
||||||
|
#include "avb_ops.h"
|
||||||
|
#include "avb_slot_verify.h"
|
||||||
|
|
||||||
|
/* Maximum allow length (in bytes) of a partition name, including
|
||||||
|
* ab_suffix.
|
||||||
|
*/
|
||||||
|
#define AVB_PART_NAME_MAX_SIZE 32
|
||||||
|
|
||||||
|
#define AVB_MAX_NUM_CMDLINE_SUBST 10
|
||||||
|
|
||||||
|
/* Holds information about command-line substitutions. */
|
||||||
|
typedef struct AvbCmdlineSubstList {
|
||||||
|
size_t size;
|
||||||
|
char* tokens[AVB_MAX_NUM_CMDLINE_SUBST];
|
||||||
|
char* values[AVB_MAX_NUM_CMDLINE_SUBST];
|
||||||
|
} AvbCmdlineSubstList;
|
||||||
|
|
||||||
|
/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
|
||||||
|
* values. Returns NULL on OOM, otherwise the cmdline with values
|
||||||
|
* replaced.
|
||||||
|
*/
|
||||||
|
char* avb_sub_cmdline(AvbOps* ops,
|
||||||
|
const char* cmdline,
|
||||||
|
const char* ab_suffix,
|
||||||
|
bool using_boot_for_vbmeta,
|
||||||
|
const AvbCmdlineSubstList* additional_substitutions);
|
||||||
|
|
||||||
|
AvbSlotVerifyResult avb_append_options(
|
||||||
|
AvbOps* ops,
|
||||||
|
AvbSlotVerifyData* slot_data,
|
||||||
|
AvbVBMetaImageHeader* toplevel_vbmeta,
|
||||||
|
AvbAlgorithmType algorithm_type,
|
||||||
|
AvbHashtreeErrorMode hashtree_error_mode);
|
||||||
|
|
||||||
|
/* Allocates and initializes a new command line substitution list. Free with
|
||||||
|
* |avb_free_cmdline_subst_list|.
|
||||||
|
*/
|
||||||
|
AvbCmdlineSubstList* avb_new_cmdline_subst_list(void);
|
||||||
|
|
||||||
|
/* Use this instead of |avb_free| to deallocate a AvbCmdlineSubstList. */
|
||||||
|
void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst);
|
||||||
|
|
||||||
|
/* Adds a hashtree root digest to be substituted in $(AVB_*_ROOT_DIGEST)
|
||||||
|
* variables. The partition name differentiates the variable. For example, if
|
||||||
|
* |part_name| is "foo" then $(AVB_FOO_ROOT_DIGEST) will be substituted with the
|
||||||
|
* hex encoding of the digest. The substitution will be added to
|
||||||
|
* |out_cmdline_subst|. Returns AVB_SLOT_VERIFY_RESULT_OK on success.
|
||||||
|
*/
|
||||||
|
AvbSlotVerifyResult avb_add_root_digest_substitution(
|
||||||
|
const char* part_name,
|
||||||
|
const uint8_t* digest,
|
||||||
|
size_t digest_size,
|
||||||
|
AvbCmdlineSubstList* out_cmdline_subst);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -355,8 +355,7 @@ static AvbAlgorithmData algorithm_data[_AVB_ALGORITHM_NUM_TYPES] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) {
|
const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) {
|
||||||
if (algorithm >= AVB_ALGORITHM_TYPE_NONE &&
|
if ((size_t)algorithm < _AVB_ALGORITHM_NUM_TYPES) {
|
||||||
algorithm < _AVB_ALGORITHM_NUM_TYPES) {
|
|
||||||
return &algorithm_data[algorithm];
|
return &algorithm_data[algorithm];
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,21 @@ extern "C" {
|
||||||
/* Size of a RSA-8192 signature. */
|
/* Size of a RSA-8192 signature. */
|
||||||
#define AVB_RSA8192_NUM_BYTES 1024
|
#define AVB_RSA8192_NUM_BYTES 1024
|
||||||
|
|
||||||
|
/* Size in bytes of a SHA-1 digest. */
|
||||||
|
#define AVB_SHA1_DIGEST_SIZE 20
|
||||||
|
|
||||||
/* Size in bytes of a SHA-256 digest. */
|
/* Size in bytes of a SHA-256 digest. */
|
||||||
#define AVB_SHA256_DIGEST_SIZE 32
|
#define AVB_SHA256_DIGEST_SIZE 32
|
||||||
|
|
||||||
/* Size in bytes of a SHA-512 digest. */
|
/* Size in bytes of a SHA-512 digest. */
|
||||||
#define AVB_SHA512_DIGEST_SIZE 64
|
#define AVB_SHA512_DIGEST_SIZE 64
|
||||||
|
|
||||||
|
/* Possible digest types supported by libavb routines. */
|
||||||
|
typedef enum {
|
||||||
|
AVB_DIGEST_TYPE_SHA256,
|
||||||
|
AVB_DIGEST_TYPE_SHA512,
|
||||||
|
} AvbDigestType;
|
||||||
|
|
||||||
/* Algorithms that can be used in the vbmeta image for
|
/* Algorithms that can be used in the vbmeta image for
|
||||||
* verification. An algorithm consists of a hash type and a signature
|
* verification. An algorithm consists of a hash type and a signature
|
||||||
* type.
|
* type.
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
|
||||||
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
|
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
|
||||||
dest->salt_len = avb_be32toh(dest->salt_len);
|
dest->salt_len = avb_be32toh(dest->salt_len);
|
||||||
dest->digest_len = avb_be32toh(dest->digest_len);
|
dest->digest_len = avb_be32toh(dest->digest_len);
|
||||||
|
dest->flags = avb_be32toh(dest->flags);
|
||||||
|
|
||||||
/* Check that partition_name, salt, and digest are fully contained. */
|
/* Check that partition_name, salt, and digest are fully contained. */
|
||||||
expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
|
expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,16 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Flags for hash descriptors.
|
||||||
|
*
|
||||||
|
* AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
|
||||||
|
* partition logic to this partition. This is intentionally a negative boolean
|
||||||
|
* because A/B should be both the default and most used in practice.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
|
||||||
|
} AvbHashDescriptorFlags;
|
||||||
|
|
||||||
/* A descriptor containing information about hash for an image.
|
/* A descriptor containing information about hash for an image.
|
||||||
*
|
*
|
||||||
* This descriptor is typically used for boot partitions to verify the
|
* This descriptor is typically used for boot partitions to verify the
|
||||||
|
|
@ -46,6 +56,10 @@ extern "C" {
|
||||||
*
|
*
|
||||||
* The |reserved| field is for future expansion and must be set to NUL
|
* The |reserved| field is for future expansion and must be set to NUL
|
||||||
* bytes.
|
* bytes.
|
||||||
|
*
|
||||||
|
* Changes in v1.1:
|
||||||
|
* - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB
|
||||||
|
* - digest_len may be zero, which indicates the use of a persistent digest
|
||||||
*/
|
*/
|
||||||
typedef struct AvbHashDescriptor {
|
typedef struct AvbHashDescriptor {
|
||||||
AvbDescriptor parent_descriptor;
|
AvbDescriptor parent_descriptor;
|
||||||
|
|
@ -54,7 +68,8 @@ typedef struct AvbHashDescriptor {
|
||||||
uint32_t partition_name_len;
|
uint32_t partition_name_len;
|
||||||
uint32_t salt_len;
|
uint32_t salt_len;
|
||||||
uint32_t digest_len;
|
uint32_t digest_len;
|
||||||
uint8_t reserved[64];
|
uint32_t flags;
|
||||||
|
uint8_t reserved[60];
|
||||||
} AVB_ATTR_PACKED AvbHashDescriptor;
|
} AVB_ATTR_PACKED AvbHashDescriptor;
|
||||||
|
|
||||||
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ bool avb_hashtree_descriptor_validate_and_byteswap(
|
||||||
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
|
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
|
||||||
dest->salt_len = avb_be32toh(dest->salt_len);
|
dest->salt_len = avb_be32toh(dest->salt_len);
|
||||||
dest->root_digest_len = avb_be32toh(dest->root_digest_len);
|
dest->root_digest_len = avb_be32toh(dest->root_digest_len);
|
||||||
|
dest->flags = avb_be32toh(dest->flags);
|
||||||
|
|
||||||
/* Check that partition_name, salt, and root_digest are fully contained. */
|
/* Check that partition_name, salt, and root_digest are fully contained. */
|
||||||
expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
|
expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,16 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Flags for hashtree descriptors.
|
||||||
|
*
|
||||||
|
* AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
|
||||||
|
* partition logic to this partition. This is intentionally a negative boolean
|
||||||
|
* because A/B should be both the default and most used in practice.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
|
||||||
|
} AvbHashtreeDescriptorFlags;
|
||||||
|
|
||||||
/* A descriptor containing information about a dm-verity hashtree.
|
/* A descriptor containing information about a dm-verity hashtree.
|
||||||
*
|
*
|
||||||
* Hash-trees are used to verify large partitions typically containing
|
* Hash-trees are used to verify large partitions typically containing
|
||||||
|
|
@ -48,6 +58,10 @@ extern "C" {
|
||||||
*
|
*
|
||||||
* The |reserved| field is for future expansion and must be set to NUL
|
* The |reserved| field is for future expansion and must be set to NUL
|
||||||
* bytes.
|
* bytes.
|
||||||
|
*
|
||||||
|
* Changes in v1.1:
|
||||||
|
* - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB
|
||||||
|
* - digest_len may be zero, which indicates the use of a persistent digest
|
||||||
*/
|
*/
|
||||||
typedef struct AvbHashtreeDescriptor {
|
typedef struct AvbHashtreeDescriptor {
|
||||||
AvbDescriptor parent_descriptor;
|
AvbDescriptor parent_descriptor;
|
||||||
|
|
@ -64,7 +78,8 @@ typedef struct AvbHashtreeDescriptor {
|
||||||
uint32_t partition_name_len;
|
uint32_t partition_name_len;
|
||||||
uint32_t salt_len;
|
uint32_t salt_len;
|
||||||
uint32_t root_digest_len;
|
uint32_t root_digest_len;
|
||||||
uint8_t reserved[64];
|
uint32_t flags;
|
||||||
|
uint8_t reserved[60];
|
||||||
} AVB_ATTR_PACKED AvbHashtreeDescriptor;
|
} AVB_ATTR_PACKED AvbHashtreeDescriptor;
|
||||||
|
|
||||||
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Well-known names of named persistent values. */
|
||||||
|
#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
|
||||||
|
|
||||||
/* Return codes used for I/O operations.
|
/* Return codes used for I/O operations.
|
||||||
*
|
*
|
||||||
* AVB_IO_RESULT_OK is returned if the requested operation was
|
* AVB_IO_RESULT_OK is returned if the requested operation was
|
||||||
|
|
@ -51,13 +54,25 @@ extern "C" {
|
||||||
* AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
|
* AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
|
||||||
* range of bytes requested to be read or written is outside the range
|
* range of bytes requested to be read or written is outside the range
|
||||||
* of the partition.
|
* of the partition.
|
||||||
|
*
|
||||||
|
* AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value
|
||||||
|
* does not exist.
|
||||||
|
*
|
||||||
|
* AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent
|
||||||
|
* value size is not supported or does not match the expected size.
|
||||||
|
*
|
||||||
|
* AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small
|
||||||
|
* for the requested operation.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
AVB_IO_RESULT_OK,
|
AVB_IO_RESULT_OK,
|
||||||
AVB_IO_RESULT_ERROR_OOM,
|
AVB_IO_RESULT_ERROR_OOM,
|
||||||
AVB_IO_RESULT_ERROR_IO,
|
AVB_IO_RESULT_ERROR_IO,
|
||||||
AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
|
AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
|
||||||
AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
|
AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
|
||||||
|
AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
|
||||||
|
AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
|
||||||
|
AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
|
||||||
} AvbIOResult;
|
} AvbIOResult;
|
||||||
|
|
||||||
struct AvbOps;
|
struct AvbOps;
|
||||||
|
|
@ -117,6 +132,27 @@ struct AvbOps {
|
||||||
void* buffer,
|
void* buffer,
|
||||||
size_t* out_num_read);
|
size_t* out_num_read);
|
||||||
|
|
||||||
|
/* Gets the starting pointer of a partition that is pre-loaded in memory, and
|
||||||
|
* save it to |out_pointer|. The preloaded partition is expected to be
|
||||||
|
* |num_bytes|, where the actual preloaded byte count is returned in
|
||||||
|
* |out_num_bytes_preloaded|. |out_num_bytes_preloaded| must be no larger than
|
||||||
|
* |num_bytes|.
|
||||||
|
*
|
||||||
|
* This provides an alternative way to access a partition that is preloaded
|
||||||
|
* into memory without a full memory copy. When this function pointer is not
|
||||||
|
* set (has value NULL), or when the |out_pointer| is set to NULL as a result,
|
||||||
|
* |read_from_partition| will be used as the fallback. This function is mainly
|
||||||
|
* used for accessing the entire partition content to calculate its hash.
|
||||||
|
*
|
||||||
|
* Preloaded partition data must outlive the lifespan of the
|
||||||
|
* |AvbSlotVerifyData| structure that |avb_slot_verify| outputs.
|
||||||
|
*/
|
||||||
|
AvbIOResult (*get_preloaded_partition)(AvbOps* ops,
|
||||||
|
const char* partition,
|
||||||
|
size_t num_bytes,
|
||||||
|
uint8_t** out_pointer,
|
||||||
|
size_t* out_num_bytes_preloaded);
|
||||||
|
|
||||||
/* Writes |num_bytes| from |bffer| at offset |offset| to partition
|
/* Writes |num_bytes| from |bffer| at offset |offset| to partition
|
||||||
* with name |partition| (NUL-terminated UTF-8 string). If |offset|
|
* with name |partition| (NUL-terminated UTF-8 string). If |offset|
|
||||||
* is negative, its absolute value should be interpreted as the
|
* is negative, its absolute value should be interpreted as the
|
||||||
|
|
@ -219,6 +255,53 @@ struct AvbOps {
|
||||||
AvbIOResult (*get_size_of_partition)(AvbOps* ops,
|
AvbIOResult (*get_size_of_partition)(AvbOps* ops,
|
||||||
const char* partition,
|
const char* partition,
|
||||||
uint64_t* out_size_num_bytes);
|
uint64_t* out_size_num_bytes);
|
||||||
|
|
||||||
|
/* Reads a persistent value corresponding to the given |name|. The value is
|
||||||
|
* returned in |out_buffer| which must point to |buffer_size| bytes. On
|
||||||
|
* success |out_num_bytes_read| contains the number of bytes read into
|
||||||
|
* |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned,
|
||||||
|
* |out_num_bytes_read| contains the number of bytes that would have been read
|
||||||
|
* which can be used to allocate a buffer.
|
||||||
|
*
|
||||||
|
* The |buffer_size| may be zero and the |out_buffer| may be NULL, but if
|
||||||
|
* |out_buffer| is NULL then |buffer_size| *must* be zero.
|
||||||
|
*
|
||||||
|
* Returns AVB_IO_RESULT_OK on success, otherwise an error code.
|
||||||
|
*
|
||||||
|
* If the value does not exist, is not supported, or is not populated, returns
|
||||||
|
* AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the
|
||||||
|
* size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE.
|
||||||
|
*
|
||||||
|
* This operation is currently only used to support persistent digests. If a
|
||||||
|
* device does not use persistent digests this function pointer can be set to
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
AvbIOResult (*read_persistent_value)(AvbOps* ops,
|
||||||
|
const char* name,
|
||||||
|
size_t buffer_size,
|
||||||
|
uint8_t* out_buffer,
|
||||||
|
size_t* out_num_bytes_read);
|
||||||
|
|
||||||
|
/* Writes a persistent value corresponding to the given |name|. The value is
|
||||||
|
* supplied in |value| which must point to |value_size| bytes. Any existing
|
||||||
|
* value with the same name is overwritten. If |value_size| is zero, future
|
||||||
|
* calls to |read_persistent_value| will return
|
||||||
|
* AVB_IO_RESULT_ERROR_NO_SUCH_VALUE.
|
||||||
|
*
|
||||||
|
* Returns AVB_IO_RESULT_OK on success, otherwise an error code.
|
||||||
|
*
|
||||||
|
* If the value |name| is not supported, returns
|
||||||
|
* AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported,
|
||||||
|
* returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE.
|
||||||
|
*
|
||||||
|
* This operation is currently only used to support persistent digests. If a
|
||||||
|
* device does not use persistent digests this function pointer can be set to
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
AvbIOResult (*write_persistent_value)(AvbOps* ops,
|
||||||
|
const char* name,
|
||||||
|
size_t value_size,
|
||||||
|
const uint8_t* value);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,16 @@
|
||||||
|
|
||||||
#include "avb_slot_verify.h"
|
#include "avb_slot_verify.h"
|
||||||
#include "avb_chain_partition_descriptor.h"
|
#include "avb_chain_partition_descriptor.h"
|
||||||
|
#include "avb_cmdline.h"
|
||||||
#include "avb_footer.h"
|
#include "avb_footer.h"
|
||||||
#include "avb_hash_descriptor.h"
|
#include "avb_hash_descriptor.h"
|
||||||
|
#include "avb_hashtree_descriptor.h"
|
||||||
#include "avb_kernel_cmdline_descriptor.h"
|
#include "avb_kernel_cmdline_descriptor.h"
|
||||||
#include "avb_sha.h"
|
#include "avb_sha.h"
|
||||||
#include "avb_util.h"
|
#include "avb_util.h"
|
||||||
#include "avb_vbmeta_image.h"
|
#include "avb_vbmeta_image.h"
|
||||||
#include "avb_version.h"
|
#include "avb_version.h"
|
||||||
|
|
||||||
/* Maximum allow length (in bytes) of a partition name, including
|
|
||||||
* ab_suffix.
|
|
||||||
*/
|
|
||||||
#define PART_NAME_MAX_SIZE 32
|
|
||||||
|
|
||||||
/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
|
/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
|
||||||
#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
|
#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
|
||||||
|
|
||||||
|
|
@ -69,6 +66,114 @@ static inline bool result_should_continue(AvbSlotVerifyResult result) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AvbSlotVerifyResult load_full_partition(AvbOps* ops,
|
||||||
|
const char* part_name,
|
||||||
|
uint64_t image_size,
|
||||||
|
uint8_t** out_image_buf,
|
||||||
|
bool* out_image_preloaded) {
|
||||||
|
size_t part_num_read;
|
||||||
|
AvbIOResult io_ret;
|
||||||
|
|
||||||
|
/* Make sure that we do not overwrite existing data. */
|
||||||
|
avb_assert(*out_image_buf == NULL);
|
||||||
|
avb_assert(!*out_image_preloaded);
|
||||||
|
|
||||||
|
/* We are going to implicitly cast image_size from uint64_t to size_t in the
|
||||||
|
* following code, so we need to make sure that the cast is safe. */
|
||||||
|
if (image_size != (size_t)(image_size)) {
|
||||||
|
avb_errorv(part_name, ": Partition size too large to load.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try use a preloaded one. */
|
||||||
|
if (ops->get_preloaded_partition != NULL) {
|
||||||
|
io_ret = ops->get_preloaded_partition(
|
||||||
|
ops, part_name, image_size, out_image_buf, &part_num_read);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*out_image_buf != NULL) {
|
||||||
|
if (part_num_read != image_size) {
|
||||||
|
avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
*out_image_preloaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate and copy the partition. */
|
||||||
|
if (!*out_image_preloaded) {
|
||||||
|
*out_image_buf = avb_malloc(image_size);
|
||||||
|
if (*out_image_buf == NULL) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_ret = ops->read_from_partition(ops,
|
||||||
|
part_name,
|
||||||
|
0 /* offset */,
|
||||||
|
image_size,
|
||||||
|
*out_image_buf,
|
||||||
|
&part_num_read);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
if (part_num_read != image_size) {
|
||||||
|
avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
|
||||||
|
const char* part_name,
|
||||||
|
size_t expected_digest_size,
|
||||||
|
uint8_t* out_digest) {
|
||||||
|
char* persistent_value_name = NULL;
|
||||||
|
AvbIOResult io_ret = AVB_IO_RESULT_OK;
|
||||||
|
size_t stored_digest_size = 0;
|
||||||
|
|
||||||
|
if (ops->read_persistent_value == NULL) {
|
||||||
|
avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
}
|
||||||
|
persistent_value_name =
|
||||||
|
avb_strdupv(AVB_NPV_PERSISTENT_DIGEST_PREFIX, part_name, NULL);
|
||||||
|
if (persistent_value_name == NULL) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
}
|
||||||
|
io_ret = ops->read_persistent_value(ops,
|
||||||
|
persistent_value_name,
|
||||||
|
expected_digest_size,
|
||||||
|
out_digest,
|
||||||
|
&stored_digest_size);
|
||||||
|
avb_free(persistent_value_name);
|
||||||
|
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
} else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) {
|
||||||
|
avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
} else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE ||
|
||||||
|
io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE ||
|
||||||
|
expected_digest_size != stored_digest_size) {
|
||||||
|
avb_errorv(
|
||||||
|
part_name, ": Persistent digest is not of expected size.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||||
|
avb_errorv(part_name, ": Error reading persistent digest.\n", NULL);
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
||||||
|
}
|
||||||
|
return AVB_SLOT_VERIFY_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static AvbSlotVerifyResult load_and_verify_hash_partition(
|
static AvbSlotVerifyResult load_and_verify_hash_partition(
|
||||||
AvbOps* ops,
|
AvbOps* ops,
|
||||||
const char* const* requested_partitions,
|
const char* const* requested_partitions,
|
||||||
|
|
@ -80,15 +185,18 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
||||||
const uint8_t* desc_partition_name = NULL;
|
const uint8_t* desc_partition_name = NULL;
|
||||||
const uint8_t* desc_salt;
|
const uint8_t* desc_salt;
|
||||||
const uint8_t* desc_digest;
|
const uint8_t* desc_digest;
|
||||||
char part_name[PART_NAME_MAX_SIZE];
|
char part_name[AVB_PART_NAME_MAX_SIZE];
|
||||||
AvbSlotVerifyResult ret;
|
AvbSlotVerifyResult ret;
|
||||||
AvbIOResult io_ret;
|
AvbIOResult io_ret;
|
||||||
uint8_t* image_buf = NULL;
|
uint8_t* image_buf = NULL;
|
||||||
size_t part_num_read;
|
bool image_preloaded = false;
|
||||||
uint8_t* digest;
|
uint8_t* digest;
|
||||||
size_t digest_len;
|
size_t digest_len;
|
||||||
const char* found;
|
const char* found;
|
||||||
uint64_t image_size;
|
uint64_t image_size;
|
||||||
|
size_t expected_digest_len = 0;
|
||||||
|
uint8_t expected_digest_buf[AVB_SHA512_DIGEST_SIZE];
|
||||||
|
const uint8_t* expected_digest = NULL;
|
||||||
|
|
||||||
if (!avb_hash_descriptor_validate_and_byteswap(
|
if (!avb_hash_descriptor_validate_and_byteswap(
|
||||||
(const AvbHashDescriptor*)descriptor, &hash_desc)) {
|
(const AvbHashDescriptor*)descriptor, &hash_desc)) {
|
||||||
|
|
@ -118,15 +226,35 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!avb_str_concat(part_name,
|
if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) {
|
||||||
sizeof part_name,
|
/* No ab_suffix, just copy the partition name as is. */
|
||||||
(const char*)desc_partition_name,
|
if (hash_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
|
||||||
hash_desc.partition_name_len,
|
avb_error("Partition name does not fit.\n");
|
||||||
ab_suffix,
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
avb_strlen(ab_suffix))) {
|
goto out;
|
||||||
avb_error("Partition name and suffix does not fit.\n");
|
}
|
||||||
|
avb_memcpy(part_name, desc_partition_name, hash_desc.partition_name_len);
|
||||||
|
part_name[hash_desc.partition_name_len] = '\0';
|
||||||
|
} else if (hash_desc.digest_len == 0 && avb_strlen(ab_suffix) != 0) {
|
||||||
|
/* No ab_suffix allowed for partitions without a digest in the descriptor
|
||||||
|
* because these partitions hold data unique to this device and are not
|
||||||
|
* updated using an A/B scheme.
|
||||||
|
*/
|
||||||
|
avb_error("Cannot use A/B with a persistent digest.\n");
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
goto out;
|
goto out;
|
||||||
|
} else {
|
||||||
|
/* Add ab_suffix to the partition name. */
|
||||||
|
if (!avb_str_concat(part_name,
|
||||||
|
sizeof part_name,
|
||||||
|
(const char*)desc_partition_name,
|
||||||
|
hash_desc.partition_name_len,
|
||||||
|
ab_suffix,
|
||||||
|
avb_strlen(ab_suffix))) {
|
||||||
|
avb_error("Partition name and suffix does not fit.\n");
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're allowing verification errors then hash_desc.image_size
|
/* If we're allowing verification errors then hash_desc.image_size
|
||||||
|
|
@ -159,25 +287,9 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
image_buf = avb_malloc(image_size);
|
ret = load_full_partition(
|
||||||
if (image_buf == NULL) {
|
ops, part_name, image_size, &image_buf, &image_preloaded);
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
io_ret = ops->read_from_partition(
|
|
||||||
ops, part_name, 0 /* offset */, image_size, image_buf, &part_num_read);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (part_num_read != image_size) {
|
|
||||||
avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL);
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,14 +313,31 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (digest_len != hash_desc.digest_len) {
|
if (hash_desc.digest_len == 0) {
|
||||||
|
// Expect a match to a persistent digest.
|
||||||
|
avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL);
|
||||||
|
expected_digest_len = digest_len;
|
||||||
|
expected_digest = expected_digest_buf;
|
||||||
|
avb_assert(expected_digest_len <= sizeof(expected_digest_buf));
|
||||||
|
ret =
|
||||||
|
read_persistent_digest(ops, part_name, digest_len, expected_digest_buf);
|
||||||
|
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Expect a match to the digest in the descriptor.
|
||||||
|
expected_digest_len = hash_desc.digest_len;
|
||||||
|
expected_digest = desc_digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digest_len != expected_digest_len) {
|
||||||
avb_errorv(
|
avb_errorv(
|
||||||
part_name, ": Digest in descriptor not of expected size.\n", NULL);
|
part_name, ": Digest in descriptor not of expected size.\n", NULL);
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) {
|
if (avb_safe_memcmp(digest, expected_digest, digest_len) != 0) {
|
||||||
avb_errorv(part_name,
|
avb_errorv(part_name,
|
||||||
": Hash of data does not match digest in descriptor.\n",
|
": Hash of data does not match digest in descriptor.\n",
|
||||||
NULL);
|
NULL);
|
||||||
|
|
@ -234,11 +363,12 @@ out:
|
||||||
loaded_partition->partition_name = avb_strdup(found);
|
loaded_partition->partition_name = avb_strdup(found);
|
||||||
loaded_partition->data_size = image_size;
|
loaded_partition->data_size = image_size;
|
||||||
loaded_partition->data = image_buf;
|
loaded_partition->data = image_buf;
|
||||||
|
loaded_partition->preloaded = image_preloaded;
|
||||||
image_buf = NULL;
|
image_buf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (image_buf != NULL) {
|
if (image_buf != NULL && !image_preloaded) {
|
||||||
avb_free(image_buf);
|
avb_free(image_buf);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -251,6 +381,7 @@ static AvbSlotVerifyResult load_requested_partitions(
|
||||||
AvbSlotVerifyData* slot_data) {
|
AvbSlotVerifyData* slot_data) {
|
||||||
AvbSlotVerifyResult ret;
|
AvbSlotVerifyResult ret;
|
||||||
uint8_t* image_buf = NULL;
|
uint8_t* image_buf = NULL;
|
||||||
|
bool image_preloaded = false;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
if (ops->get_size_of_partition == NULL) {
|
if (ops->get_size_of_partition == NULL) {
|
||||||
|
|
@ -260,10 +391,9 @@ static AvbSlotVerifyResult load_requested_partitions(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n = 0; requested_partitions[n] != NULL; n++) {
|
for (n = 0; requested_partitions[n] != NULL; n++) {
|
||||||
char part_name[PART_NAME_MAX_SIZE];
|
char part_name[AVB_PART_NAME_MAX_SIZE];
|
||||||
AvbIOResult io_ret;
|
AvbIOResult io_ret;
|
||||||
uint64_t image_size;
|
uint64_t image_size;
|
||||||
size_t part_num_read;
|
|
||||||
AvbPartitionData* loaded_partition;
|
AvbPartitionData* loaded_partition;
|
||||||
|
|
||||||
if (!avb_str_concat(part_name,
|
if (!avb_str_concat(part_name,
|
||||||
|
|
@ -288,25 +418,9 @@ static AvbSlotVerifyResult load_requested_partitions(
|
||||||
}
|
}
|
||||||
avb_debugv(part_name, ": Loading entire partition.\n", NULL);
|
avb_debugv(part_name, ": Loading entire partition.\n", NULL);
|
||||||
|
|
||||||
image_buf = avb_malloc(image_size);
|
ret = load_full_partition(
|
||||||
if (image_buf == NULL) {
|
ops, part_name, image_size, &image_buf, &image_preloaded);
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
io_ret = ops->read_from_partition(
|
|
||||||
ops, part_name, 0 /* offset */, image_size, image_buf, &part_num_read);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (part_num_read != image_size) {
|
|
||||||
avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL);
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,16 +438,21 @@ static AvbSlotVerifyResult load_requested_partitions(
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
loaded_partition->data_size = image_size;
|
loaded_partition->data_size = image_size;
|
||||||
loaded_partition->data = image_buf;
|
loaded_partition->data = image_buf; /* Transferring the owner. */
|
||||||
|
loaded_partition->preloaded = image_preloaded;
|
||||||
image_buf = NULL;
|
image_buf = NULL;
|
||||||
|
image_preloaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_OK;
|
ret = AVB_SLOT_VERIFY_RESULT_OK;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (image_buf != NULL) {
|
/* Free the current buffer if any. */
|
||||||
|
if (image_buf != NULL && !image_preloaded) {
|
||||||
avb_free(image_buf);
|
avb_free(image_buf);
|
||||||
}
|
}
|
||||||
|
/* Buffers that are already saved in slot_data will be handled by the caller
|
||||||
|
* even on failure. */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,8 +468,9 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
||||||
const uint8_t* expected_public_key,
|
const uint8_t* expected_public_key,
|
||||||
size_t expected_public_key_length,
|
size_t expected_public_key_length,
|
||||||
AvbSlotVerifyData* slot_data,
|
AvbSlotVerifyData* slot_data,
|
||||||
AvbAlgorithmType* out_algorithm_type) {
|
AvbAlgorithmType* out_algorithm_type,
|
||||||
char full_partition_name[PART_NAME_MAX_SIZE];
|
AvbCmdlineSubstList* out_additional_cmdline_subst) {
|
||||||
|
char full_partition_name[AVB_PART_NAME_MAX_SIZE];
|
||||||
AvbSlotVerifyResult ret;
|
AvbSlotVerifyResult ret;
|
||||||
AvbIOResult io_ret;
|
AvbIOResult io_ret;
|
||||||
size_t vbmeta_offset;
|
size_t vbmeta_offset;
|
||||||
|
|
@ -485,7 +605,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
||||||
NULL /* expected_public_key */,
|
NULL /* expected_public_key */,
|
||||||
0 /* expected_public_key_length */,
|
0 /* expected_public_key_length */,
|
||||||
slot_data,
|
slot_data,
|
||||||
out_algorithm_type);
|
out_algorithm_type,
|
||||||
|
out_additional_cmdline_subst);
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
|
avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
|
||||||
|
|
@ -681,7 +802,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
||||||
* checks that it matches what's in the hash descriptor.
|
* checks that it matches what's in the hash descriptor.
|
||||||
*
|
*
|
||||||
* - hashtree descriptor: Do nothing since verification happens
|
* - hashtree descriptor: Do nothing since verification happens
|
||||||
* on-the-fly from within the OS.
|
* on-the-fly from within the OS. (Unless the descriptor uses a
|
||||||
|
* persistent digest, in which case we need to find it).
|
||||||
*
|
*
|
||||||
* - chained partition descriptor: Load the footer, load the vbmeta
|
* - chained partition descriptor: Load the footer, load the vbmeta
|
||||||
* image, verify vbmeta image (includes rollback checks, hash
|
* image, verify vbmeta image (includes rollback checks, hash
|
||||||
|
|
@ -752,18 +874,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
||||||
sizeof(AvbChainPartitionDescriptor);
|
sizeof(AvbChainPartitionDescriptor);
|
||||||
chain_public_key = chain_partition_name + chain_desc.partition_name_len;
|
chain_public_key = chain_partition_name + chain_desc.partition_name_len;
|
||||||
|
|
||||||
sub_ret = load_and_verify_vbmeta(ops,
|
sub_ret =
|
||||||
requested_partitions,
|
load_and_verify_vbmeta(ops,
|
||||||
ab_suffix,
|
requested_partitions,
|
||||||
allow_verification_error,
|
ab_suffix,
|
||||||
toplevel_vbmeta_flags,
|
allow_verification_error,
|
||||||
chain_desc.rollback_index_location,
|
toplevel_vbmeta_flags,
|
||||||
(const char*)chain_partition_name,
|
chain_desc.rollback_index_location,
|
||||||
chain_desc.partition_name_len,
|
(const char*)chain_partition_name,
|
||||||
chain_public_key,
|
chain_desc.partition_name_len,
|
||||||
chain_desc.public_key_len,
|
chain_public_key,
|
||||||
slot_data,
|
chain_desc.public_key_len,
|
||||||
NULL /* out_algorithm_type */);
|
slot_data,
|
||||||
|
NULL, /* out_algorithm_type */
|
||||||
|
NULL /* out_additional_cmdline_subst */);
|
||||||
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
ret = sub_ret;
|
ret = sub_ret;
|
||||||
if (!result_should_continue(ret)) {
|
if (!result_should_continue(ret)) {
|
||||||
|
|
@ -849,9 +973,90 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
/* Explicit fall-through */
|
case AVB_DESCRIPTOR_TAG_HASHTREE: {
|
||||||
|
AvbHashtreeDescriptor hashtree_desc;
|
||||||
|
|
||||||
|
if (!avb_hashtree_descriptor_validate_and_byteswap(
|
||||||
|
(AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) {
|
||||||
|
avb_errorv(
|
||||||
|
full_partition_name, ": Hashtree descriptor is invalid.\n", NULL);
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We only need to continue when there is no digest in the descriptor.
|
||||||
|
* This is because the only processing here is to find the digest and
|
||||||
|
* make it available on the kernel command line.
|
||||||
|
*/
|
||||||
|
if (hashtree_desc.root_digest_len == 0) {
|
||||||
|
char part_name[AVB_PART_NAME_MAX_SIZE];
|
||||||
|
size_t digest_len = 0;
|
||||||
|
uint8_t digest_buf[AVB_SHA512_DIGEST_SIZE];
|
||||||
|
const uint8_t* desc_partition_name =
|
||||||
|
((const uint8_t*)descriptors[n]) + sizeof(AvbHashtreeDescriptor);
|
||||||
|
|
||||||
|
if (!avb_validate_utf8(desc_partition_name,
|
||||||
|
hashtree_desc.partition_name_len)) {
|
||||||
|
avb_error("Partition name is not valid UTF-8.\n");
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No ab_suffix for partitions without a digest in the descriptor
|
||||||
|
* because these partitions hold data unique to this device and are
|
||||||
|
* not updated using an A/B scheme.
|
||||||
|
*/
|
||||||
|
if ((hashtree_desc.flags &
|
||||||
|
AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) == 0 &&
|
||||||
|
avb_strlen(ab_suffix) != 0) {
|
||||||
|
avb_error("Cannot use A/B with a persistent root digest.\n");
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (hashtree_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
|
||||||
|
avb_error("Partition name does not fit.\n");
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
avb_memcpy(
|
||||||
|
part_name, desc_partition_name, hashtree_desc.partition_name_len);
|
||||||
|
part_name[hashtree_desc.partition_name_len] = '\0';
|
||||||
|
|
||||||
|
/* Determine the expected digest size from the hash algorithm. */
|
||||||
|
if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, "sha1") ==
|
||||||
|
0) {
|
||||||
|
digest_len = AVB_SHA1_DIGEST_SIZE;
|
||||||
|
} else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
|
||||||
|
"sha256") == 0) {
|
||||||
|
digest_len = AVB_SHA256_DIGEST_SIZE;
|
||||||
|
} else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
|
||||||
|
"sha512") == 0) {
|
||||||
|
digest_len = AVB_SHA512_DIGEST_SIZE;
|
||||||
|
} else {
|
||||||
|
avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
|
||||||
|
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_additional_cmdline_subst) {
|
||||||
|
ret =
|
||||||
|
avb_add_root_digest_substitution(part_name,
|
||||||
|
digest_buf,
|
||||||
|
digest_len,
|
||||||
|
out_additional_cmdline_subst);
|
||||||
|
if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case AVB_DESCRIPTOR_TAG_PROPERTY:
|
case AVB_DESCRIPTOR_TAG_PROPERTY:
|
||||||
case AVB_DESCRIPTOR_TAG_HASHTREE:
|
|
||||||
/* Do nothing. */
|
/* Do nothing. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -886,350 +1091,6 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NUM_GUIDS 3
|
|
||||||
|
|
||||||
/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
|
|
||||||
* values. Returns NULL on OOM, otherwise the cmdline with values
|
|
||||||
* replaced.
|
|
||||||
*/
|
|
||||||
static char* sub_cmdline(AvbOps* ops,
|
|
||||||
const char* cmdline,
|
|
||||||
const char* ab_suffix,
|
|
||||||
bool using_boot_for_vbmeta) {
|
|
||||||
const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
|
|
||||||
const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
|
|
||||||
"$(ANDROID_BOOT_PARTUUID)",
|
|
||||||
"$(ANDROID_VBMETA_PARTUUID)"};
|
|
||||||
char* ret = NULL;
|
|
||||||
AvbIOResult io_ret;
|
|
||||||
|
|
||||||
/* Special-case for when the top-level vbmeta struct is in the boot
|
|
||||||
* partition.
|
|
||||||
*/
|
|
||||||
if (using_boot_for_vbmeta) {
|
|
||||||
part_name_str[2] = "boot";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Replace unique partition GUIDs */
|
|
||||||
for (size_t n = 0; n < NUM_GUIDS; n++) {
|
|
||||||
char part_name[PART_NAME_MAX_SIZE];
|
|
||||||
char guid_buf[37];
|
|
||||||
|
|
||||||
if (!avb_str_concat(part_name,
|
|
||||||
sizeof part_name,
|
|
||||||
part_name_str[n],
|
|
||||||
avb_strlen(part_name_str[n]),
|
|
||||||
ab_suffix,
|
|
||||||
avb_strlen(ab_suffix))) {
|
|
||||||
avb_error("Partition name and suffix does not fit.\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
io_ret = ops->get_unique_guid_for_partition(
|
|
||||||
ops, part_name, guid_buf, sizeof guid_buf);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
return NULL;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_error("Error getting unique GUID for partition.\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == NULL) {
|
|
||||||
ret = avb_replace(cmdline, replace_str[n], guid_buf);
|
|
||||||
} else {
|
|
||||||
char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
|
|
||||||
avb_free(ret);
|
|
||||||
ret = new_ret;
|
|
||||||
}
|
|
||||||
if (ret == NULL) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
if (ret != NULL) {
|
|
||||||
avb_free(ret);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmdline_append_option(AvbSlotVerifyData* slot_data,
|
|
||||||
const char* key,
|
|
||||||
const char* value) {
|
|
||||||
size_t offset, key_len, value_len;
|
|
||||||
char* new_cmdline;
|
|
||||||
|
|
||||||
key_len = avb_strlen(key);
|
|
||||||
value_len = avb_strlen(value);
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
if (slot_data->cmdline != NULL) {
|
|
||||||
offset = avb_strlen(slot_data->cmdline);
|
|
||||||
if (offset > 0) {
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_cmdline = avb_calloc(offset + key_len + value_len + 2);
|
|
||||||
if (new_cmdline == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (offset > 0) {
|
|
||||||
avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
|
|
||||||
new_cmdline[offset - 1] = ' ';
|
|
||||||
}
|
|
||||||
avb_memcpy(new_cmdline + offset, key, key_len);
|
|
||||||
new_cmdline[offset + key_len] = '=';
|
|
||||||
avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
|
|
||||||
if (slot_data->cmdline != NULL) {
|
|
||||||
avb_free(slot_data->cmdline);
|
|
||||||
}
|
|
||||||
slot_data->cmdline = new_cmdline;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define AVB_MAX_DIGITS_UINT64 32
|
|
||||||
|
|
||||||
/* Writes |value| to |digits| in base 10 followed by a NUL byte.
|
|
||||||
* Returns number of characters written excluding the NUL byte.
|
|
||||||
*/
|
|
||||||
static size_t uint64_to_base10(uint64_t value,
|
|
||||||
char digits[AVB_MAX_DIGITS_UINT64]) {
|
|
||||||
char rev_digits[AVB_MAX_DIGITS_UINT64];
|
|
||||||
size_t n, num_digits;
|
|
||||||
|
|
||||||
for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
|
|
||||||
rev_digits[num_digits++] = (value % 10) + '0';
|
|
||||||
value /= 10;
|
|
||||||
if (value == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (n = 0; n < num_digits; n++) {
|
|
||||||
digits[n] = rev_digits[num_digits - 1 - n];
|
|
||||||
}
|
|
||||||
digits[n] = '\0';
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmdline_append_version(AvbSlotVerifyData* slot_data,
|
|
||||||
const char* key,
|
|
||||||
uint64_t major_version,
|
|
||||||
uint64_t minor_version) {
|
|
||||||
char major_digits[AVB_MAX_DIGITS_UINT64];
|
|
||||||
char minor_digits[AVB_MAX_DIGITS_UINT64];
|
|
||||||
char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
|
|
||||||
size_t num_major_digits, num_minor_digits;
|
|
||||||
|
|
||||||
num_major_digits = uint64_to_base10(major_version, major_digits);
|
|
||||||
num_minor_digits = uint64_to_base10(minor_version, minor_digits);
|
|
||||||
avb_memcpy(combined, major_digits, num_major_digits);
|
|
||||||
combined[num_major_digits] = '.';
|
|
||||||
avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
|
|
||||||
combined[num_major_digits + 1 + num_minor_digits] = '\0';
|
|
||||||
|
|
||||||
return cmdline_append_option(slot_data, key, combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
|
|
||||||
const char* key,
|
|
||||||
uint64_t value) {
|
|
||||||
char digits[AVB_MAX_DIGITS_UINT64];
|
|
||||||
uint64_to_base10(value, digits);
|
|
||||||
return cmdline_append_option(slot_data, key, digits);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
|
|
||||||
const char* key,
|
|
||||||
const uint8_t* data,
|
|
||||||
size_t data_len) {
|
|
||||||
char hex_digits[17] = "0123456789abcdef";
|
|
||||||
char* hex_data;
|
|
||||||
int ret;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
hex_data = avb_malloc(data_len * 2 + 1);
|
|
||||||
if (hex_data == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (n = 0; n < data_len; n++) {
|
|
||||||
hex_data[n * 2] = hex_digits[data[n] >> 4];
|
|
||||||
hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
|
|
||||||
}
|
|
||||||
hex_data[n * 2] = '\0';
|
|
||||||
|
|
||||||
ret = cmdline_append_option(slot_data, key, hex_data);
|
|
||||||
avb_free(hex_data);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AvbSlotVerifyResult append_options(
|
|
||||||
AvbOps* ops,
|
|
||||||
AvbSlotVerifyData* slot_data,
|
|
||||||
AvbVBMetaImageHeader* toplevel_vbmeta,
|
|
||||||
AvbAlgorithmType algorithm_type,
|
|
||||||
AvbHashtreeErrorMode hashtree_error_mode) {
|
|
||||||
AvbSlotVerifyResult ret;
|
|
||||||
const char* verity_mode = NULL;
|
|
||||||
bool is_device_unlocked;
|
|
||||||
AvbIOResult io_ret;
|
|
||||||
|
|
||||||
/* Add androidboot.vbmeta.device option. */
|
|
||||||
if (!cmdline_append_option(slot_data,
|
|
||||||
"androidboot.vbmeta.device",
|
|
||||||
"PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add androidboot.vbmeta.avb_version option. */
|
|
||||||
if (!cmdline_append_version(slot_data,
|
|
||||||
"androidboot.vbmeta.avb_version",
|
|
||||||
AVB_VERSION_MAJOR,
|
|
||||||
AVB_VERSION_MINOR)) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set androidboot.avb.device_state to "locked" or "unlocked". */
|
|
||||||
io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_error("Error getting device state.\n");
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (!cmdline_append_option(slot_data,
|
|
||||||
"androidboot.vbmeta.device_state",
|
|
||||||
is_device_unlocked ? "unlocked" : "locked")) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
|
|
||||||
* function as is used to sign vbmeta.
|
|
||||||
*/
|
|
||||||
switch (algorithm_type) {
|
|
||||||
/* Explicit fallthrough. */
|
|
||||||
case AVB_ALGORITHM_TYPE_NONE:
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
|
|
||||||
AvbSHA256Ctx ctx;
|
|
||||||
size_t n, total_size = 0;
|
|
||||||
avb_sha256_init(&ctx);
|
|
||||||
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
|
|
||||||
avb_sha256_update(&ctx,
|
|
||||||
slot_data->vbmeta_images[n].vbmeta_data,
|
|
||||||
slot_data->vbmeta_images[n].vbmeta_size);
|
|
||||||
total_size += slot_data->vbmeta_images[n].vbmeta_size;
|
|
||||||
}
|
|
||||||
if (!cmdline_append_option(
|
|
||||||
slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
|
|
||||||
!cmdline_append_uint64_base10(
|
|
||||||
slot_data, "androidboot.vbmeta.size", total_size) ||
|
|
||||||
!cmdline_append_hex(slot_data,
|
|
||||||
"androidboot.vbmeta.digest",
|
|
||||||
avb_sha256_final(&ctx),
|
|
||||||
AVB_SHA256_DIGEST_SIZE)) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
/* Explicit fallthrough. */
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
|
|
||||||
case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
|
|
||||||
AvbSHA512Ctx ctx;
|
|
||||||
size_t n, total_size = 0;
|
|
||||||
avb_sha512_init(&ctx);
|
|
||||||
for (n = 0; n < slot_data->num_vbmeta_images; n++) {
|
|
||||||
avb_sha512_update(&ctx,
|
|
||||||
slot_data->vbmeta_images[n].vbmeta_data,
|
|
||||||
slot_data->vbmeta_images[n].vbmeta_size);
|
|
||||||
total_size += slot_data->vbmeta_images[n].vbmeta_size;
|
|
||||||
}
|
|
||||||
if (!cmdline_append_option(
|
|
||||||
slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
|
|
||||||
!cmdline_append_uint64_base10(
|
|
||||||
slot_data, "androidboot.vbmeta.size", total_size) ||
|
|
||||||
!cmdline_append_hex(slot_data,
|
|
||||||
"androidboot.vbmeta.digest",
|
|
||||||
avb_sha512_final(&ctx),
|
|
||||||
AVB_SHA512_DIGEST_SIZE)) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case _AVB_ALGORITHM_NUM_TYPES:
|
|
||||||
avb_assert_not_reached();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
|
|
||||||
if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
|
|
||||||
verity_mode = "disabled";
|
|
||||||
} else {
|
|
||||||
const char* dm_verity_mode = NULL;
|
|
||||||
char* new_ret;
|
|
||||||
|
|
||||||
switch (hashtree_error_mode) {
|
|
||||||
case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
|
|
||||||
if (!cmdline_append_option(
|
|
||||||
slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
verity_mode = "enforcing";
|
|
||||||
dm_verity_mode = "restart_on_corruption";
|
|
||||||
break;
|
|
||||||
case AVB_HASHTREE_ERROR_MODE_RESTART:
|
|
||||||
verity_mode = "enforcing";
|
|
||||||
dm_verity_mode = "restart_on_corruption";
|
|
||||||
break;
|
|
||||||
case AVB_HASHTREE_ERROR_MODE_EIO:
|
|
||||||
verity_mode = "eio";
|
|
||||||
/* For now there's no option to specify the EIO mode. So
|
|
||||||
* just use 'ignore_zero_blocks' since that's already set
|
|
||||||
* and dm-verity-target.c supports specifying this multiple
|
|
||||||
* times.
|
|
||||||
*/
|
|
||||||
dm_verity_mode = "ignore_zero_blocks";
|
|
||||||
break;
|
|
||||||
case AVB_HASHTREE_ERROR_MODE_LOGGING:
|
|
||||||
verity_mode = "logging";
|
|
||||||
dm_verity_mode = "ignore_corruption";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
new_ret = avb_replace(
|
|
||||||
slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
|
|
||||||
avb_free(slot_data->cmdline);
|
|
||||||
slot_data->cmdline = new_ret;
|
|
||||||
if (slot_data->cmdline == NULL) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cmdline_append_option(
|
|
||||||
slot_data, "androidboot.veritymode", verity_mode)) {
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_OK;
|
|
||||||
|
|
||||||
out:
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
const char* const* requested_partitions,
|
const char* const* requested_partitions,
|
||||||
const char* ab_suffix,
|
const char* ab_suffix,
|
||||||
|
|
@ -1243,6 +1104,7 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
AvbVBMetaImageHeader toplevel_vbmeta;
|
AvbVBMetaImageHeader toplevel_vbmeta;
|
||||||
bool allow_verification_error =
|
bool allow_verification_error =
|
||||||
(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
||||||
|
AvbCmdlineSubstList* additional_cmdline_subst = NULL;
|
||||||
|
|
||||||
/* Fail early if we're missing the AvbOps needed for slot verification.
|
/* Fail early if we're missing the AvbOps needed for slot verification.
|
||||||
*
|
*
|
||||||
|
|
@ -1254,7 +1116,6 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
avb_assert(ops->validate_vbmeta_public_key != NULL);
|
avb_assert(ops->validate_vbmeta_public_key != NULL);
|
||||||
avb_assert(ops->read_rollback_index != NULL);
|
avb_assert(ops->read_rollback_index != NULL);
|
||||||
avb_assert(ops->get_unique_guid_for_partition != NULL);
|
avb_assert(ops->get_unique_guid_for_partition != NULL);
|
||||||
/* avb_assert(ops->get_size_of_partition != NULL); */
|
|
||||||
|
|
||||||
if (out_data != NULL) {
|
if (out_data != NULL) {
|
||||||
*out_data = NULL;
|
*out_data = NULL;
|
||||||
|
|
@ -1288,6 +1149,12 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
additional_cmdline_subst = avb_new_cmdline_subst_list();
|
||||||
|
if (additional_cmdline_subst == NULL) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
ret = load_and_verify_vbmeta(ops,
|
ret = load_and_verify_vbmeta(ops,
|
||||||
requested_partitions,
|
requested_partitions,
|
||||||
ab_suffix,
|
ab_suffix,
|
||||||
|
|
@ -1299,7 +1166,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
NULL /* expected_public_key */,
|
NULL /* expected_public_key */,
|
||||||
0 /* expected_public_key_length */,
|
0 /* expected_public_key_length */,
|
||||||
slot_data,
|
slot_data,
|
||||||
&algorithm_type);
|
&algorithm_type,
|
||||||
|
additional_cmdline_subst);
|
||||||
if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -1341,14 +1209,14 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Add options - any failure in append_options() is either an
|
/* Add options - any failure in avb_append_options() is either an
|
||||||
* I/O or OOM error.
|
* I/O or OOM error.
|
||||||
*/
|
*/
|
||||||
AvbSlotVerifyResult sub_ret = append_options(ops,
|
AvbSlotVerifyResult sub_ret = avb_append_options(ops,
|
||||||
slot_data,
|
slot_data,
|
||||||
&toplevel_vbmeta,
|
&toplevel_vbmeta,
|
||||||
algorithm_type,
|
algorithm_type,
|
||||||
hashtree_error_mode);
|
hashtree_error_mode);
|
||||||
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||||
ret = sub_ret;
|
ret = sub_ret;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1358,14 +1226,19 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
|
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
|
||||||
if (slot_data->cmdline != NULL) {
|
if (slot_data->cmdline != NULL) {
|
||||||
char* new_cmdline;
|
char* new_cmdline;
|
||||||
new_cmdline = sub_cmdline(
|
new_cmdline = avb_sub_cmdline(ops,
|
||||||
ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta);
|
slot_data->cmdline,
|
||||||
if (new_cmdline == NULL) {
|
ab_suffix,
|
||||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
using_boot_for_vbmeta,
|
||||||
goto fail;
|
additional_cmdline_subst);
|
||||||
|
if (new_cmdline != slot_data->cmdline) {
|
||||||
|
if (new_cmdline == NULL) {
|
||||||
|
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
avb_free(slot_data->cmdline);
|
||||||
|
slot_data->cmdline = new_cmdline;
|
||||||
}
|
}
|
||||||
avb_free(slot_data->cmdline);
|
|
||||||
slot_data->cmdline = new_cmdline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_data != NULL) {
|
if (out_data != NULL) {
|
||||||
|
|
@ -1375,6 +1248,9 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avb_free_cmdline_subst_list(additional_cmdline_subst);
|
||||||
|
additional_cmdline_subst = NULL;
|
||||||
|
|
||||||
if (!allow_verification_error) {
|
if (!allow_verification_error) {
|
||||||
avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
|
avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
|
||||||
}
|
}
|
||||||
|
|
@ -1385,6 +1261,9 @@ fail:
|
||||||
if (slot_data != NULL) {
|
if (slot_data != NULL) {
|
||||||
avb_slot_verify_data_free(slot_data);
|
avb_slot_verify_data_free(slot_data);
|
||||||
}
|
}
|
||||||
|
if (additional_cmdline_subst != NULL) {
|
||||||
|
avb_free_cmdline_subst_list(additional_cmdline_subst);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1415,7 +1294,7 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data) {
|
||||||
if (loaded_partition->partition_name != NULL) {
|
if (loaded_partition->partition_name != NULL) {
|
||||||
avb_free(loaded_partition->partition_name);
|
avb_free(loaded_partition->partition_name);
|
||||||
}
|
}
|
||||||
if (loaded_partition->data != NULL) {
|
if (loaded_partition->data != NULL && !loaded_partition->preloaded) {
|
||||||
avb_free(loaded_partition->data);
|
avb_free(loaded_partition->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1465,3 +1344,42 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
|
||||||
|
AvbDigestType digest_type,
|
||||||
|
uint8_t* out_digest) {
|
||||||
|
bool ret = false;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
switch (digest_type) {
|
||||||
|
case AVB_DIGEST_TYPE_SHA256: {
|
||||||
|
AvbSHA256Ctx ctx;
|
||||||
|
avb_sha256_init(&ctx);
|
||||||
|
for (n = 0; n < data->num_vbmeta_images; n++) {
|
||||||
|
avb_sha256_update(&ctx,
|
||||||
|
data->vbmeta_images[n].vbmeta_data,
|
||||||
|
data->vbmeta_images[n].vbmeta_size);
|
||||||
|
}
|
||||||
|
avb_memcpy(out_digest, avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE);
|
||||||
|
ret = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case AVB_DIGEST_TYPE_SHA512: {
|
||||||
|
AvbSHA512Ctx ctx;
|
||||||
|
avb_sha512_init(&ctx);
|
||||||
|
for (n = 0; n < data->num_vbmeta_images; n++) {
|
||||||
|
avb_sha512_update(&ctx,
|
||||||
|
data->vbmeta_images[n].vbmeta_data,
|
||||||
|
data->vbmeta_images[n].vbmeta_size);
|
||||||
|
}
|
||||||
|
avb_memcpy(out_digest, avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE);
|
||||||
|
ret = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
/* Do not add a 'default:' case here because of -Wswitch. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
avb_fatal("Unknown digest type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,10 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result);
|
||||||
/* AvbPartitionData contains data loaded from partitions when using
|
/* AvbPartitionData contains data loaded from partitions when using
|
||||||
* avb_slot_verify(). The |partition_name| field contains the name of
|
* avb_slot_verify(). The |partition_name| field contains the name of
|
||||||
* the partition (without A/B suffix), |data| points to the loaded
|
* the partition (without A/B suffix), |data| points to the loaded
|
||||||
* data which is |data_size| bytes long.
|
* data which is |data_size| bytes long. If |preloaded| is set to true,
|
||||||
|
* this structure dose not own |data|. The caller of |avb_slot_verify|
|
||||||
|
* needs to make sure that the preloaded data outlives this
|
||||||
|
* |AvbPartitionData| structure.
|
||||||
*
|
*
|
||||||
* Note that this is strictly less than the partition size - it's only
|
* Note that this is strictly less than the partition size - it's only
|
||||||
* the image stored there, not the entire partition nor any of the
|
* the image stored there, not the entire partition nor any of the
|
||||||
|
|
@ -124,6 +127,7 @@ typedef struct {
|
||||||
char* partition_name;
|
char* partition_name;
|
||||||
uint8_t* data;
|
uint8_t* data;
|
||||||
size_t data_size;
|
size_t data_size;
|
||||||
|
bool preloaded;
|
||||||
} AvbPartitionData;
|
} AvbPartitionData;
|
||||||
|
|
||||||
/* AvbVBMetaData contains a vbmeta struct loaded from a partition when
|
/* AvbVBMetaData contains a vbmeta struct loaded from a partition when
|
||||||
|
|
@ -256,9 +260,15 @@ typedef struct {
|
||||||
uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
|
uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
|
||||||
} AvbSlotVerifyData;
|
} AvbSlotVerifyData;
|
||||||
|
|
||||||
/* Fast version of avb_slot_verify_data_free, this method will not
|
/* Calculates a digest of all vbmeta images in |data| using
|
||||||
* free bootimage */
|
* the digest indicated by |digest_type|. Stores the result
|
||||||
void avb_slot_verify_data_free_fast(AvbSlotVerifyData* data);
|
* in |out_digest| which must be large enough to hold a digest
|
||||||
|
* of the requested type.
|
||||||
|
*/
|
||||||
|
void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
|
||||||
|
AvbDigestType digest_type,
|
||||||
|
uint8_t* out_digest);
|
||||||
|
|
||||||
/* Frees a |AvbSlotVerifyData| including all data it points to. */
|
/* Frees a |AvbSlotVerifyData| including all data it points to. */
|
||||||
void avb_slot_verify_data_free(AvbSlotVerifyData* data);
|
void avb_slot_verify_data_free(AvbSlotVerifyData* data);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,10 @@ void avb_free(void* ptr);
|
||||||
/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
|
/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
|
||||||
size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
|
size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/* Divide the |dividend| by 10 and saves back to the pointer. Return the
|
||||||
|
* remainder. */
|
||||||
|
uint32_t avb_div_by_10(uint64_t* dividend);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -76,3 +76,9 @@ void* avb_malloc_(size_t size) {
|
||||||
void avb_free(void* ptr) {
|
void avb_free(void* ptr) {
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t avb_div_by_10(uint64_t* dividend) {
|
||||||
|
uint32_t rem = (uint32_t)(*dividend % 10);
|
||||||
|
*dividend /= 10;
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ char* avb_replace(const char* str, const char* search, const char* replace) {
|
||||||
char* new_str;
|
char* new_str;
|
||||||
num_new = ret_len + num_before + replace_len + 1;
|
num_new = ret_len + num_before + replace_len + 1;
|
||||||
new_str = avb_malloc(num_new);
|
new_str = avb_malloc(num_new);
|
||||||
if (ret == NULL) {
|
if (new_str == NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
avb_memcpy(new_str, ret, ret_len);
|
avb_memcpy(new_str, ret, ret_len);
|
||||||
|
|
@ -324,7 +324,7 @@ char* avb_replace(const char* str, const char* search, const char* replace) {
|
||||||
size_t num_remaining = avb_strlen(str_after_last_replace);
|
size_t num_remaining = avb_strlen(str_after_last_replace);
|
||||||
size_t num_new = ret_len + num_remaining + 1;
|
size_t num_new = ret_len + num_remaining + 1;
|
||||||
char* new_str = avb_malloc(num_new);
|
char* new_str = avb_malloc(num_new);
|
||||||
if (ret == NULL) {
|
if (new_str == NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
avb_memcpy(new_str, ret, ret_len);
|
avb_memcpy(new_str, ret, ret_len);
|
||||||
|
|
@ -401,3 +401,30 @@ const char* avb_basename(const char* str) {
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void avb_uppercase(char* str) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; str[i] != '\0'; ++i) {
|
||||||
|
if (str[i] <= 0x7A && str[i] >= 0x61) {
|
||||||
|
str[i] -= 0x20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* avb_bin2hex(const uint8_t* data, size_t data_len) {
|
||||||
|
const char hex_digits[17] = "0123456789abcdef";
|
||||||
|
char* hex_data;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
hex_data = avb_malloc(data_len * 2 + 1);
|
||||||
|
if (hex_data == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < data_len; n++) {
|
||||||
|
hex_data[n * 2] = hex_digits[data[n] >> 4];
|
||||||
|
hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
|
||||||
|
}
|
||||||
|
hex_data[n * 2] = '\0';
|
||||||
|
return hex_data;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,16 @@ uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
|
||||||
*/
|
*/
|
||||||
const char* avb_basename(const char* str);
|
const char* avb_basename(const char* str);
|
||||||
|
|
||||||
|
/* Converts any ascii lowercase characters in |str| to uppercase in-place.
|
||||||
|
* |str| must be NUL-terminated and valid UTF-8.
|
||||||
|
*/
|
||||||
|
void avb_uppercase(char* str);
|
||||||
|
|
||||||
|
/* Converts |data_len| bytes of |data| to hex and returns the result. Returns
|
||||||
|
* NULL on OOM. Caller must free the returned string with avb_free.
|
||||||
|
*/
|
||||||
|
char* avb_bin2hex(const uint8_t* data, size_t data_len);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ extern "C" {
|
||||||
|
|
||||||
/* The version number of AVB - keep in sync with avbtool. */
|
/* The version number of AVB - keep in sync with avbtool. */
|
||||||
#define AVB_VERSION_MAJOR 1
|
#define AVB_VERSION_MAJOR 1
|
||||||
#define AVB_VERSION_MINOR 0
|
#define AVB_VERSION_MINOR 1
|
||||||
#define AVB_VERSION_SUB 0
|
#define AVB_VERSION_SUB 0
|
||||||
|
|
||||||
/* Returns a NUL-terminated string for the libavb version in use. The
|
/* Returns a NUL-terminated string for the libavb version in use. The
|
||||||
|
|
|
||||||
|
|
@ -406,307 +406,6 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For legacy i.mx6/7, we won't enable A/B due to the limitation of
|
|
||||||
* storage capacity, but we still want to verify boot/recovery with
|
|
||||||
* AVB. */
|
|
||||||
AvbABFlowResult avb_single_flow(AvbABOps* ab_ops,
|
|
||||||
const char* const* requested_partitions,
|
|
||||||
AvbSlotVerifyFlags flags,
|
|
||||||
AvbHashtreeErrorMode hashtree_error_mode,
|
|
||||||
AvbSlotVerifyData** out_data) {
|
|
||||||
AvbOps* ops = ab_ops->ops;
|
|
||||||
AvbSlotVerifyData* slot_data = NULL;
|
|
||||||
AvbSlotVerifyData* data = NULL;
|
|
||||||
AvbABFlowResult ret;
|
|
||||||
bool saw_and_allowed_verification_error = false;
|
|
||||||
|
|
||||||
/* Validate boot/recovery. */
|
|
||||||
AvbSlotVerifyResult verify_result;
|
|
||||||
|
|
||||||
verify_result = avb_slot_verify(ops,
|
|
||||||
requested_partitions,
|
|
||||||
"",
|
|
||||||
flags,
|
|
||||||
hashtree_error_mode,
|
|
||||||
&slot_data);
|
|
||||||
switch (verify_result) {
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_OK:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
|
|
||||||
/* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
|
||||||
* these mean game over.
|
|
||||||
*/
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* explicit fallthrough. */
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
|
|
||||||
if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
|
|
||||||
/* Do nothing since we allow this. */
|
|
||||||
avb_debugv("Allowing slot ",
|
|
||||||
slot_suffixes[n],
|
|
||||||
" which verified "
|
|
||||||
"with result ",
|
|
||||||
avb_slot_verify_result_to_string(verify_result),
|
|
||||||
" because "
|
|
||||||
"AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
|
|
||||||
"is set.\n",
|
|
||||||
NULL);
|
|
||||||
saw_and_allowed_verification_error = true;
|
|
||||||
} else {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
/* Do not add a 'default:' case here because of -Wswitch. */
|
|
||||||
}
|
|
||||||
|
|
||||||
avb_assert(slot_data != NULL);
|
|
||||||
data = slot_data;
|
|
||||||
slot_data = NULL;
|
|
||||||
if (saw_and_allowed_verification_error) {
|
|
||||||
avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
|
||||||
ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
|
|
||||||
} else {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (slot_data != NULL) {
|
|
||||||
avb_slot_verify_data_free(slot_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_data != NULL) {
|
|
||||||
*out_data = data;
|
|
||||||
} else {
|
|
||||||
if (data != NULL) {
|
|
||||||
avb_slot_verify_data_free(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops,
|
|
||||||
const char* const* requested_partitions,
|
|
||||||
AvbSlotVerifyFlags flags,
|
|
||||||
AvbHashtreeErrorMode hashtree_error_mode,
|
|
||||||
AvbSlotVerifyData** out_data) {
|
|
||||||
AvbOps* ops = ab_ops->ops;
|
|
||||||
AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
|
|
||||||
AvbSlotVerifyData* data = NULL;
|
|
||||||
AvbABFlowResult ret;
|
|
||||||
AvbABData ab_data, ab_data_orig;
|
|
||||||
size_t slot_index_to_boot, n;
|
|
||||||
AvbIOResult io_ret;
|
|
||||||
bool saw_and_allowed_verification_error = false;
|
|
||||||
size_t target_slot;
|
|
||||||
AvbSlotVerifyResult verify_result;
|
|
||||||
bool set_slot_unbootable = false;
|
|
||||||
|
|
||||||
io_ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot_index_to_boot = 2; // Means not 0 or 1
|
|
||||||
target_slot = (ab_data.slots[1].priority > ab_data.slots[0].priority? 1 : 0);
|
|
||||||
|
|
||||||
for (n = 0; n < 2; n++) {
|
|
||||||
if (!slot_is_bootable(&ab_data.slots[target_slot])) {
|
|
||||||
target_slot = (target_slot == 1 ? 0 : 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
verify_result = avb_slot_verify(ops,
|
|
||||||
requested_partitions,
|
|
||||||
slot_suffixes[target_slot],
|
|
||||||
flags,
|
|
||||||
hashtree_error_mode,
|
|
||||||
&slot_data[target_slot]);
|
|
||||||
switch (verify_result) {
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_OK:
|
|
||||||
slot_index_to_boot = target_slot;
|
|
||||||
n = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
|
|
||||||
/* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
|
||||||
* these mean game over.
|
|
||||||
*/
|
|
||||||
set_slot_unbootable = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* explicit fallthrough. */
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
|
|
||||||
if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
|
|
||||||
/* Do nothing since we allow this. */
|
|
||||||
avb_debugv("Allowing slot ",
|
|
||||||
slot_suffixes[target_slot],
|
|
||||||
" which verified "
|
|
||||||
"with result ",
|
|
||||||
avb_slot_verify_result_to_string(verify_result),
|
|
||||||
" because "
|
|
||||||
"AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
|
|
||||||
"is set.\n",
|
|
||||||
NULL);
|
|
||||||
saw_and_allowed_verification_error = true;
|
|
||||||
slot_index_to_boot = target_slot;
|
|
||||||
n = 2;
|
|
||||||
} else {
|
|
||||||
set_slot_unbootable = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
|
|
||||||
goto out;
|
|
||||||
/* Do not add a 'default:' case here because of -Wswitch. */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (set_slot_unbootable) {
|
|
||||||
avb_errorv("Error verifying slot ",
|
|
||||||
slot_suffixes[target_slot],
|
|
||||||
" with result ",
|
|
||||||
avb_slot_verify_result_to_string(verify_result),
|
|
||||||
" - setting unbootable.\n",
|
|
||||||
NULL);
|
|
||||||
slot_set_unbootable(&ab_data.slots[target_slot]);
|
|
||||||
set_slot_unbootable = false;
|
|
||||||
}
|
|
||||||
/* switch to another slot */
|
|
||||||
target_slot = (target_slot == 1 ? 0 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot_index_to_boot == 2) {
|
|
||||||
/* No bootable slots! */
|
|
||||||
avb_error("No bootable slots found.\n");
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update stored rollback index such that the stored rollback index
|
|
||||||
* is the largest value supporting all currently bootable slots. Do
|
|
||||||
* this for every rollback index location.
|
|
||||||
*/
|
|
||||||
for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
|
|
||||||
uint64_t rollback_index_value = 0;
|
|
||||||
|
|
||||||
if (slot_data[0] != NULL && slot_data[1] != NULL) {
|
|
||||||
uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n];
|
|
||||||
uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n];
|
|
||||||
rollback_index_value =
|
|
||||||
(a_rollback_index < b_rollback_index ? a_rollback_index
|
|
||||||
: b_rollback_index);
|
|
||||||
} else if (slot_data[0] != NULL) {
|
|
||||||
rollback_index_value = slot_data[0]->rollback_indexes[n];
|
|
||||||
} else if (slot_data[1] != NULL) {
|
|
||||||
rollback_index_value = slot_data[1]->rollback_indexes[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rollback_index_value != 0) {
|
|
||||||
uint64_t current_rollback_index_value;
|
|
||||||
io_ret = ops->read_rollback_index(ops, n, ¤t_rollback_index_value);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_error("Error getting rollback index for slot.\n");
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (current_rollback_index_value != rollback_index_value) {
|
|
||||||
io_ret = ops->write_rollback_index(ops, n, rollback_index_value);
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
goto out;
|
|
||||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
avb_error("Error setting stored rollback index.\n");
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally, select this slot. */
|
|
||||||
avb_assert(slot_data[slot_index_to_boot] != NULL);
|
|
||||||
data = slot_data[slot_index_to_boot];
|
|
||||||
slot_data[slot_index_to_boot] = NULL;
|
|
||||||
if (saw_and_allowed_verification_error) {
|
|
||||||
avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
|
|
||||||
ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
|
|
||||||
} else {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ... and decrement tries remaining, if applicable. */
|
|
||||||
if (!ab_data.slots[slot_index_to_boot].successful_boot &&
|
|
||||||
ab_data.slots[slot_index_to_boot].tries_remaining > 0) {
|
|
||||||
ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
io_ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
|
|
||||||
if (io_ret != AVB_IO_RESULT_OK) {
|
|
||||||
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
|
|
||||||
} else {
|
|
||||||
ret = AVB_AB_FLOW_RESULT_ERROR_IO;
|
|
||||||
}
|
|
||||||
if (data != NULL) {
|
|
||||||
avb_slot_verify_data_free(data);
|
|
||||||
data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (n = 0; n < 2; n++) {
|
|
||||||
if (slot_data[n] != NULL) {
|
|
||||||
avb_slot_verify_data_free(slot_data[n]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_data != NULL) {
|
|
||||||
*out_data = data;
|
|
||||||
} else {
|
|
||||||
if (data != NULL) {
|
|
||||||
avb_slot_verify_data_free(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops,
|
AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops,
|
||||||
unsigned int slot_number) {
|
unsigned int slot_number) {
|
||||||
AvbABData ab_data, ab_data_orig;
|
AvbABData ab_data, ab_data_orig;
|
||||||
|
|
|
||||||
|
|
@ -223,19 +223,6 @@ AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops,
|
||||||
AvbHashtreeErrorMode hashtree_error_mode,
|
AvbHashtreeErrorMode hashtree_error_mode,
|
||||||
AvbSlotVerifyData** out_data);
|
AvbSlotVerifyData** out_data);
|
||||||
|
|
||||||
AvbABFlowResult avb_ab_flow_fast(AvbABOps* ab_ops,
|
|
||||||
const char* const* requested_partitions,
|
|
||||||
AvbSlotVerifyFlags flags,
|
|
||||||
AvbHashtreeErrorMode hashtree_error_mode,
|
|
||||||
AvbSlotVerifyData** out_data);
|
|
||||||
/* This is for legacy i.mx6/7 which don't enable A/B but want to
|
|
||||||
* verify boot/recovery with AVB */
|
|
||||||
AvbABFlowResult avb_single_flow(AvbABOps* ab_ops,
|
|
||||||
const char* const* requested_partitions,
|
|
||||||
AvbSlotVerifyFlags flags,
|
|
||||||
AvbHashtreeErrorMode hashtree_error_mode,
|
|
||||||
AvbSlotVerifyData** out_data);
|
|
||||||
|
|
||||||
/* Marks the slot with the given slot number as active. Returns
|
/* Marks the slot with the given slot number as active. Returns
|
||||||
* AVB_IO_RESULT_OK on success, error code otherwise.
|
* AVB_IO_RESULT_OK on success, error code otherwise.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#ifndef AVB_AB_OPS_H_
|
#ifndef AVB_AB_OPS_H_
|
||||||
#define AVB_AB_OPS_H_
|
#define AVB_AB_OPS_H_
|
||||||
|
|
||||||
#include "../libavb/libavb.h"
|
#include <libavb/libavb.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,18 @@
|
||||||
#ifndef LIBAVB_AB_H_
|
#ifndef LIBAVB_AB_H_
|
||||||
#define LIBAVB_AB_H_
|
#define LIBAVB_AB_H_
|
||||||
|
|
||||||
#include "../libavb/libavb.h"
|
#include <libavb/libavb.h>
|
||||||
|
|
||||||
|
/* The libavb_ab/ and boot_control/ code has been marked for some time
|
||||||
|
* as experimental in anticipation of being removed in the future. It
|
||||||
|
* is now deprecated and to continue using it you must define
|
||||||
|
* AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED. It will be removed Jun
|
||||||
|
* 1 2018.
|
||||||
|
*/
|
||||||
|
#ifndef AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
|
||||||
|
#error \
|
||||||
|
"You must define AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED to use this library."
|
||||||
|
#endif
|
||||||
|
|
||||||
/* The AVB_INSIDE_LIBAVB_AB_H preprocessor symbol is used to enforce
|
/* The AVB_INSIDE_LIBAVB_AB_H preprocessor symbol is used to enforce
|
||||||
* library users to include only this file. All public interfaces, and
|
* library users to include only this file. All public interfaces, and
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@
|
||||||
#ifndef AVB_ATX_OPS_H_
|
#ifndef AVB_ATX_OPS_H_
|
||||||
#define AVB_ATX_OPS_H_
|
#define AVB_ATX_OPS_H_
|
||||||
|
|
||||||
#include "../libavb/libavb.h"
|
#include <libavb/libavb.h>
|
||||||
|
|
||||||
#include "../libavb_atx/avb_atx_types.h"
|
#include "avb_atx_types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -59,6 +59,22 @@ struct AvbAtxOps {
|
||||||
*/
|
*/
|
||||||
AvbIOResult (*read_permanent_attributes_hash)(
|
AvbIOResult (*read_permanent_attributes_hash)(
|
||||||
AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
|
AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
|
||||||
|
|
||||||
|
/* Provides the key version of a key used during verification. This may be
|
||||||
|
* useful for managing the minimum key version.
|
||||||
|
*/
|
||||||
|
void (*set_key_version)(AvbAtxOps* atx_ops,
|
||||||
|
size_t rollback_index_location,
|
||||||
|
uint64_t key_version);
|
||||||
|
|
||||||
|
/* Generates |num_bytes| random bytes and stores them in |output|,
|
||||||
|
* which must point to a buffer large enough to store the bytes.
|
||||||
|
*
|
||||||
|
* Returns AVB_IO_RESULT_OK on success, otherwise an error code.
|
||||||
|
*/
|
||||||
|
AvbIOResult (*get_random)(AvbAtxOps* atx_ops,
|
||||||
|
size_t num_bytes,
|
||||||
|
uint8_t* output);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#ifndef AVB_ATX_TYPES_H_
|
#ifndef AVB_ATX_TYPES_H_
|
||||||
#define AVB_ATX_TYPES_H_
|
#define AVB_ATX_TYPES_H_
|
||||||
|
|
||||||
#include "../libavb/libavb.h"
|
#include <libavb/libavb.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -39,6 +39,9 @@ extern "C" {
|
||||||
/* Size in bytes of an Android Things product ID. */
|
/* Size in bytes of an Android Things product ID. */
|
||||||
#define AVB_ATX_PRODUCT_ID_SIZE 16
|
#define AVB_ATX_PRODUCT_ID_SIZE 16
|
||||||
|
|
||||||
|
/* Size in bytes of an Android Things unlock challenge. */
|
||||||
|
#define AVB_ATX_UNLOCK_CHALLENGE_SIZE 16
|
||||||
|
|
||||||
/* Size in bytes of a serialized public key with a 4096-bit modulus. */
|
/* Size in bytes of a serialized public key with a 4096-bit modulus. */
|
||||||
#define AVB_ATX_PUBLIC_KEY_SIZE (sizeof(AvbRSAPublicKeyHeader) + 1024)
|
#define AVB_ATX_PUBLIC_KEY_SIZE (sizeof(AvbRSAPublicKeyHeader) + 1024)
|
||||||
|
|
||||||
|
|
@ -71,6 +74,21 @@ typedef struct AvbAtxPublicKeyMetadata {
|
||||||
AvbAtxCertificate product_signing_key_certificate;
|
AvbAtxCertificate product_signing_key_certificate;
|
||||||
} AVB_ATTR_PACKED AvbAtxPublicKeyMetadata;
|
} AVB_ATTR_PACKED AvbAtxPublicKeyMetadata;
|
||||||
|
|
||||||
|
/* Data structure of an Android Things unlock challenge. */
|
||||||
|
typedef struct AvbAtxUnlockChallenge {
|
||||||
|
uint32_t version;
|
||||||
|
uint8_t product_id_hash[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
uint8_t challenge[AVB_ATX_UNLOCK_CHALLENGE_SIZE];
|
||||||
|
} AVB_ATTR_PACKED AvbAtxUnlockChallenge;
|
||||||
|
|
||||||
|
/* Data structure of an Android Things unlock credential. */
|
||||||
|
typedef struct AvbAtxUnlockCredential {
|
||||||
|
uint32_t version;
|
||||||
|
AvbAtxCertificate product_intermediate_key_certificate;
|
||||||
|
AvbAtxCertificate product_unlock_key_certificate;
|
||||||
|
uint8_t challenge_signature[AVB_RSA4096_NUM_BYTES];
|
||||||
|
} AVB_ATTR_PACKED AvbAtxUnlockCredential;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,15 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../libavb_atx/avb_atx_validate.h"
|
#include "avb_atx_validate.h"
|
||||||
|
|
||||||
#include "../libavb/avb_rsa.h"
|
#include <libavb/avb_rsa.h>
|
||||||
#include "../libavb/avb_sha.h"
|
#include <libavb/avb_sha.h>
|
||||||
#include "../libavb/avb_sysdeps.h"
|
#include <libavb/avb_sysdeps.h>
|
||||||
#include "../libavb/avb_util.h"
|
#include <libavb/avb_util.h>
|
||||||
|
|
||||||
|
/* The most recent unlock challenge generated. */
|
||||||
|
static uint8_t last_unlock_challenge[AVB_ATX_UNLOCK_CHALLENGE_SIZE];
|
||||||
|
|
||||||
/* Computes the SHA256 |hash| of |length| bytes of |data|. */
|
/* Computes the SHA256 |hash| of |length| bytes of |data|. */
|
||||||
static void sha256(const uint8_t* data,
|
static void sha256(const uint8_t* data,
|
||||||
|
|
@ -59,7 +62,7 @@ static void sha256_str(const char* str, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
|
||||||
/* Verifies structure and |expected_hash| of permanent |attributes|. */
|
/* Verifies structure and |expected_hash| of permanent |attributes|. */
|
||||||
static bool verify_permanent_attributes(
|
static bool verify_permanent_attributes(
|
||||||
const AvbAtxPermanentAttributes* attributes,
|
const AvbAtxPermanentAttributes* attributes,
|
||||||
uint8_t expected_hash[AVB_SHA256_DIGEST_SIZE]) {
|
const uint8_t expected_hash[AVB_SHA256_DIGEST_SIZE]) {
|
||||||
uint8_t hash[AVB_SHA256_DIGEST_SIZE];
|
uint8_t hash[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
|
||||||
if (attributes->version != 1) {
|
if (attributes->version != 1) {
|
||||||
|
|
@ -75,10 +78,11 @@ static bool verify_permanent_attributes(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verifies the format, key version, usage, and signature of a certificate. */
|
/* Verifies the format, key version, usage, and signature of a certificate. */
|
||||||
static bool verify_certificate(AvbAtxCertificate* certificate,
|
static bool verify_certificate(
|
||||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
const AvbAtxCertificate* certificate,
|
||||||
uint64_t minimum_key_version,
|
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||||
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]) {
|
uint64_t minimum_key_version,
|
||||||
|
const uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]) {
|
||||||
const AvbAlgorithmData* algorithm_data;
|
const AvbAlgorithmData* algorithm_data;
|
||||||
uint8_t certificate_hash[AVB_SHA512_DIGEST_SIZE];
|
uint8_t certificate_hash[AVB_SHA512_DIGEST_SIZE];
|
||||||
|
|
||||||
|
|
@ -115,9 +119,10 @@ static bool verify_certificate(AvbAtxCertificate* certificate,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verifies signature and fields of a PIK certificate. */
|
/* Verifies signature and fields of a PIK certificate. */
|
||||||
static bool verify_pik_certificate(AvbAtxCertificate* certificate,
|
static bool verify_pik_certificate(
|
||||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
const AvbAtxCertificate* certificate,
|
||||||
uint64_t minimum_version) {
|
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||||
|
uint64_t minimum_version) {
|
||||||
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
|
||||||
sha256_str("com.google.android.things.vboot.ca", expected_usage);
|
sha256_str("com.google.android.things.vboot.ca", expected_usage);
|
||||||
|
|
@ -131,10 +136,10 @@ static bool verify_pik_certificate(AvbAtxCertificate* certificate,
|
||||||
|
|
||||||
/* Verifies signature and fields of a PSK certificate. */
|
/* Verifies signature and fields of a PSK certificate. */
|
||||||
static bool verify_psk_certificate(
|
static bool verify_psk_certificate(
|
||||||
AvbAtxCertificate* certificate,
|
const AvbAtxCertificate* certificate,
|
||||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||||
uint64_t minimum_version,
|
uint64_t minimum_version,
|
||||||
uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) {
|
const uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) {
|
||||||
uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
|
uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
|
||||||
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
|
||||||
|
|
@ -148,7 +153,32 @@ static bool verify_psk_certificate(
|
||||||
if (0 != avb_safe_memcmp(certificate->signed_data.subject,
|
if (0 != avb_safe_memcmp(certificate->signed_data.subject,
|
||||||
expected_subject,
|
expected_subject,
|
||||||
AVB_SHA256_DIGEST_SIZE)) {
|
AVB_SHA256_DIGEST_SIZE)) {
|
||||||
avb_error("Product ID mismatch.\n");
|
avb_error("PSK: Product ID mismatch.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verifies signature and fields of a PUK certificate. */
|
||||||
|
static bool verify_puk_certificate(
|
||||||
|
const AvbAtxCertificate* certificate,
|
||||||
|
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||||
|
uint64_t minimum_version,
|
||||||
|
const uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) {
|
||||||
|
uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
|
||||||
|
sha256_str("com.google.android.things.vboot.unlock", expected_usage);
|
||||||
|
if (!verify_certificate(
|
||||||
|
certificate, authority, minimum_version, expected_usage)) {
|
||||||
|
avb_error("Invalid PUK certificate.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sha256(product_id, AVB_ATX_PRODUCT_ID_SIZE, expected_subject);
|
||||||
|
if (0 != avb_safe_memcmp(certificate->signed_data.subject,
|
||||||
|
expected_subject,
|
||||||
|
AVB_SHA256_DIGEST_SIZE)) {
|
||||||
|
avb_error("PUK: Product ID mismatch.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -241,6 +271,131 @@ AvbIOResult avb_atx_validate_vbmeta_public_key(
|
||||||
return AVB_IO_RESULT_OK;
|
return AVB_IO_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Report the key versions used during verification. */
|
||||||
|
ops->atx_ops->set_key_version(
|
||||||
|
ops->atx_ops,
|
||||||
|
AVB_ATX_PIK_VERSION_LOCATION,
|
||||||
|
metadata.product_intermediate_key_certificate.signed_data.key_version);
|
||||||
|
ops->atx_ops->set_key_version(
|
||||||
|
ops->atx_ops,
|
||||||
|
AVB_ATX_PSK_VERSION_LOCATION,
|
||||||
|
metadata.product_signing_key_certificate.signed_data.key_version);
|
||||||
|
|
||||||
|
*out_is_trusted = true;
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbIOResult avb_atx_generate_unlock_challenge(
|
||||||
|
AvbAtxOps* atx_ops, AvbAtxUnlockChallenge* out_unlock_challenge) {
|
||||||
|
AvbIOResult result = AVB_IO_RESULT_OK;
|
||||||
|
AvbAtxPermanentAttributes permanent_attributes;
|
||||||
|
|
||||||
|
/* We need the permanent attributes to compute the product_id_hash. */
|
||||||
|
result = atx_ops->read_permanent_attributes(atx_ops, &permanent_attributes);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to read permanent attributes.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = atx_ops->get_random(
|
||||||
|
atx_ops, AVB_ATX_UNLOCK_CHALLENGE_SIZE, last_unlock_challenge);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to generate random challenge.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
out_unlock_challenge->version = 1;
|
||||||
|
sha256(permanent_attributes.product_id,
|
||||||
|
AVB_ATX_PRODUCT_ID_SIZE,
|
||||||
|
out_unlock_challenge->product_id_hash);
|
||||||
|
avb_memcpy(out_unlock_challenge->challenge,
|
||||||
|
last_unlock_challenge,
|
||||||
|
AVB_ATX_UNLOCK_CHALLENGE_SIZE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvbIOResult avb_atx_validate_unlock_credential(
|
||||||
|
AvbAtxOps* atx_ops,
|
||||||
|
const AvbAtxUnlockCredential* unlock_credential,
|
||||||
|
bool* out_is_trusted) {
|
||||||
|
AvbIOResult result = AVB_IO_RESULT_OK;
|
||||||
|
AvbAtxPermanentAttributes permanent_attributes;
|
||||||
|
uint8_t permanent_attributes_hash[AVB_SHA256_DIGEST_SIZE];
|
||||||
|
uint64_t minimum_version;
|
||||||
|
const AvbAlgorithmData* algorithm_data;
|
||||||
|
uint8_t challenge_hash[AVB_SHA512_DIGEST_SIZE];
|
||||||
|
|
||||||
|
/* Be pessimistic so we can exit early without having to remember to clear.
|
||||||
|
*/
|
||||||
|
*out_is_trusted = false;
|
||||||
|
|
||||||
|
/* Sanity check the credential. */
|
||||||
|
if (unlock_credential->version != 1) {
|
||||||
|
avb_error("Unsupported unlock credential format.\n");
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and verify permanent attributes. */
|
||||||
|
result = atx_ops->read_permanent_attributes(atx_ops, &permanent_attributes);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to read permanent attributes.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = atx_ops->read_permanent_attributes_hash(atx_ops,
|
||||||
|
permanent_attributes_hash);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to read permanent attributes hash.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (!verify_permanent_attributes(&permanent_attributes,
|
||||||
|
permanent_attributes_hash)) {
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify the PIK certificate. */
|
||||||
|
result = atx_ops->ops->read_rollback_index(
|
||||||
|
atx_ops->ops, AVB_ATX_PIK_VERSION_LOCATION, &minimum_version);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to read PIK minimum version.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (!verify_pik_certificate(
|
||||||
|
&unlock_credential->product_intermediate_key_certificate,
|
||||||
|
permanent_attributes.product_root_public_key,
|
||||||
|
minimum_version)) {
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify the PUK certificate. The minimum version is shared with the PSK. */
|
||||||
|
result = atx_ops->ops->read_rollback_index(
|
||||||
|
atx_ops->ops, AVB_ATX_PSK_VERSION_LOCATION, &minimum_version);
|
||||||
|
if (result != AVB_IO_RESULT_OK) {
|
||||||
|
avb_error("Failed to read PSK minimum version.\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (!verify_puk_certificate(
|
||||||
|
&unlock_credential->product_unlock_key_certificate,
|
||||||
|
unlock_credential->product_intermediate_key_certificate.signed_data
|
||||||
|
.public_key,
|
||||||
|
minimum_version,
|
||||||
|
permanent_attributes.product_id)) {
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify the challenge signature. */
|
||||||
|
algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA512_RSA4096);
|
||||||
|
sha512(last_unlock_challenge, AVB_ATX_UNLOCK_CHALLENGE_SIZE, challenge_hash);
|
||||||
|
if (!avb_rsa_verify(unlock_credential->product_unlock_key_certificate
|
||||||
|
.signed_data.public_key,
|
||||||
|
AVB_ATX_PUBLIC_KEY_SIZE,
|
||||||
|
unlock_credential->challenge_signature,
|
||||||
|
AVB_RSA4096_NUM_BYTES,
|
||||||
|
challenge_hash,
|
||||||
|
AVB_SHA512_DIGEST_SIZE,
|
||||||
|
algorithm_data->padding,
|
||||||
|
algorithm_data->padding_len)) {
|
||||||
|
avb_error("Invalid unlock challenge signature.\n");
|
||||||
|
return AVB_IO_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
*out_is_trusted = true;
|
*out_is_trusted = true;
|
||||||
return AVB_IO_RESULT_OK;
|
return AVB_IO_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@
|
||||||
#ifndef AVB_ATX_VALIDATE_H_
|
#ifndef AVB_ATX_VALIDATE_H_
|
||||||
#define AVB_ATX_VALIDATE_H_
|
#define AVB_ATX_VALIDATE_H_
|
||||||
|
|
||||||
#include "../libavb_atx/avb_atx_ops.h"
|
#include "avb_atx_ops.h"
|
||||||
#include "../libavb_atx/avb_atx_types.h"
|
#include "avb_atx_types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -70,6 +70,20 @@ AvbIOResult avb_atx_validate_vbmeta_public_key(
|
||||||
size_t public_key_metadata_length,
|
size_t public_key_metadata_length,
|
||||||
bool* out_is_trusted);
|
bool* out_is_trusted);
|
||||||
|
|
||||||
|
/* Generates a challenge which can be used to create an unlock credential. */
|
||||||
|
AvbIOResult avb_atx_generate_unlock_challenge(
|
||||||
|
AvbAtxOps* atx_ops, AvbAtxUnlockChallenge* out_unlock_challenge);
|
||||||
|
|
||||||
|
/* Validates an unlock credential. The certificate validation is very similar to
|
||||||
|
* the validation of public key metadata except in place of the PSK is a Product
|
||||||
|
* Unlock Key (PUK) and the certificate usage field identifies it as such. The
|
||||||
|
* challenge signature field is verified against this PUK.
|
||||||
|
*/
|
||||||
|
AvbIOResult avb_atx_validate_unlock_credential(
|
||||||
|
AvbAtxOps* atx_ops,
|
||||||
|
const AvbAtxUnlockCredential* unlock_credential,
|
||||||
|
bool* out_is_trusted);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
#ifndef LIBAVB_ATX_H_
|
#ifndef LIBAVB_ATX_H_
|
||||||
#define LIBAVB_ATX_H_
|
#define LIBAVB_ATX_H_
|
||||||
|
|
||||||
#include "../libavb/libavb.h"
|
#include <libavb/libavb.h>
|
||||||
|
|
||||||
/* The AVB_INSIDE_LIBAVB_ATX_H preprocessor symbol is used to enforce
|
/* The AVB_INSIDE_LIBAVB_ATX_H preprocessor symbol is used to enforce
|
||||||
* library users to include only this file. All public interfaces, and
|
* library users to include only this file. All public interfaces, and
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue