lib: avb: update avb source code
according to google lib avb commit id: 44e07124afb1f46af0d745d83481f49c482900b1 Change-Id: Ie59a7265699e3e6b1673bb64da6d1c7a1e7b6201 Signed-off-by: Jason Zhu <jason.zhu@rock-chips.com>
This commit is contained in:
parent
62b1148596
commit
ab608f806e
|
@ -68,6 +68,15 @@ struct AvbAtxOps {
|
|||
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
|
||||
|
|
|
@ -41,6 +41,9 @@ extern "C" {
|
|||
/* Size in bytes of an Android Things product ID. */
|
||||
#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. */
|
||||
#define AVB_ATX_PUBLIC_KEY_SIZE (sizeof(AvbRSAPublicKeyHeader) + 1024)
|
||||
|
||||
|
@ -73,6 +76,21 @@ typedef struct AvbAtxPublicKeyMetadata {
|
|||
AvbAtxCertificate product_signing_key_certificate;
|
||||
} 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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -72,6 +72,20 @@ AvbIOResult avb_atx_validate_vbmeta_public_key(
|
|||
size_t public_key_metadata_length,
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -45,12 +45,24 @@
|
|||
*/
|
||||
#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);
|
||||
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,
|
||||
|
@ -59,4 +71,24 @@ AvbSlotVerifyResult avb_append_options(
|
|||
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
|
||||
|
|
|
@ -46,12 +46,21 @@ extern "C" {
|
|||
/* Size of a RSA-8192 signature. */
|
||||
#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. */
|
||||
#define AVB_SHA256_DIGEST_SIZE 32
|
||||
|
||||
/* Size in bytes of a SHA-512 digest. */
|
||||
#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
|
||||
* verification. An algorithm consists of a hash type and a signature
|
||||
* type.
|
||||
|
|
|
@ -37,6 +37,16 @@
|
|||
extern "C" {
|
||||
#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.
|
||||
*
|
||||
* This descriptor is typically used for boot partitions to verify the
|
||||
|
@ -48,6 +58,10 @@ extern "C" {
|
|||
*
|
||||
* The |reserved| field is for future expansion and must be set to NUL
|
||||
* 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 {
|
||||
AvbDescriptor parent_descriptor;
|
||||
|
@ -56,7 +70,8 @@ typedef struct AvbHashDescriptor {
|
|||
uint32_t partition_name_len;
|
||||
uint32_t salt_len;
|
||||
uint32_t digest_len;
|
||||
uint8_t reserved[64];
|
||||
uint32_t flags;
|
||||
uint8_t reserved[60];
|
||||
} AVB_ATTR_PACKED AvbHashDescriptor;
|
||||
|
||||
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
||||
|
|
|
@ -37,6 +37,16 @@
|
|||
extern "C" {
|
||||
#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.
|
||||
*
|
||||
* Hash-trees are used to verify large partitions typically containing
|
||||
|
@ -50,6 +60,10 @@ extern "C" {
|
|||
*
|
||||
* The |reserved| field is for future expansion and must be set to NUL
|
||||
* 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 {
|
||||
AvbDescriptor parent_descriptor;
|
||||
|
@ -66,7 +80,8 @@ typedef struct AvbHashtreeDescriptor {
|
|||
uint32_t partition_name_len;
|
||||
uint32_t salt_len;
|
||||
uint32_t root_digest_len;
|
||||
uint8_t reserved[64];
|
||||
uint32_t flags;
|
||||
uint8_t reserved[60];
|
||||
} AVB_ATTR_PACKED AvbHashtreeDescriptor;
|
||||
|
||||
/* Copies |src| to |dest| and validates, byte-swapping fields in the
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Well-known names of named persistent values. */
|
||||
#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
|
||||
|
||||
/* Return codes used for I/O operations.
|
||||
*
|
||||
* AVB_IO_RESULT_OK is returned if the requested operation was
|
||||
|
@ -53,13 +56,25 @@ extern "C" {
|
|||
* AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
|
||||
* range of bytes requested to be read or written is outside the range
|
||||
* 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 {
|
||||
AVB_IO_RESULT_OK,
|
||||
AVB_IO_RESULT_ERROR_OOM,
|
||||
AVB_IO_RESULT_ERROR_IO,
|
||||
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;
|
||||
|
||||
struct AvbOps;
|
||||
|
@ -247,6 +262,53 @@ struct AvbOps {
|
|||
AvbIOResult (*get_size_of_partition)(AvbOps* ops,
|
||||
const char* partition,
|
||||
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
|
||||
|
|
|
@ -262,6 +262,15 @@ typedef struct {
|
|||
uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
|
||||
} AvbSlotVerifyData;
|
||||
|
||||
/* Calculates a digest of all vbmeta images in |data| using
|
||||
* the digest indicated by |digest_type|. Stores the result
|
||||
* 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. */
|
||||
void avb_slot_verify_data_free(AvbSlotVerifyData* data);
|
||||
|
||||
|
|
|
@ -272,6 +272,16 @@ uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
|
|||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -39,7 +39,7 @@ extern "C" {
|
|||
|
||||
/* The version number of AVB - keep in sync with avbtool. */
|
||||
#define AVB_VERSION_MAJOR 1
|
||||
#define AVB_VERSION_MINOR 0
|
||||
#define AVB_VERSION_MINOR 1
|
||||
#define AVB_VERSION_SUB 0
|
||||
|
||||
/* Returns a NUL-terminated string for the libavb version in use. The
|
||||
|
|
|
@ -33,8 +33,11 @@
|
|||
* 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) {
|
||||
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)",
|
||||
|
@ -68,7 +71,7 @@ char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
|
|||
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;
|
||||
goto fail;
|
||||
} else if (io_ret != AVB_IO_RESULT_OK) {
|
||||
avb_error("Error getting unique GUID for partition.\n");
|
||||
goto fail;
|
||||
|
@ -86,6 +89,22 @@ char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
|
|||
}
|
||||
}
|
||||
|
||||
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:
|
||||
|
@ -186,22 +205,11 @@ 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);
|
||||
char* hex_data = avb_bin2hex(data, data_len);
|
||||
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;
|
||||
|
@ -261,13 +269,11 @@ AvbSlotVerifyResult avb_append_options(
|
|||
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);
|
||||
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++) {
|
||||
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(
|
||||
|
@ -276,7 +282,7 @@ AvbSlotVerifyResult avb_append_options(
|
|||
slot_data, "androidboot.vbmeta.size", total_size) ||
|
||||
!cmdline_append_hex(slot_data,
|
||||
"androidboot.vbmeta.digest",
|
||||
avb_sha256_final(&ctx),
|
||||
vbmeta_digest,
|
||||
AVB_SHA256_DIGEST_SIZE)) {
|
||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||
goto out;
|
||||
|
@ -286,13 +292,11 @@ AvbSlotVerifyResult avb_append_options(
|
|||
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);
|
||||
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++) {
|
||||
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(
|
||||
|
@ -301,7 +305,7 @@ AvbSlotVerifyResult avb_append_options(
|
|||
slot_data, "androidboot.vbmeta.size", total_size) ||
|
||||
!cmdline_append_hex(slot_data,
|
||||
"androidboot.vbmeta.digest",
|
||||
avb_sha512_final(&ctx),
|
||||
vbmeta_digest,
|
||||
AVB_SHA512_DIGEST_SIZE)) {
|
||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||
goto out;
|
||||
|
@ -369,3 +373,68 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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->salt_len = avb_be32toh(dest->salt_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. */
|
||||
expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
|
||||
|
|
|
@ -52,6 +52,7 @@ bool avb_hashtree_descriptor_validate_and_byteswap(
|
|||
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
|
||||
dest->salt_len = avb_be32toh(dest->salt_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. */
|
||||
expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <android_avb/avb_cmdline.h>
|
||||
#include <android_avb/avb_footer.h>
|
||||
#include <android_avb/avb_hash_descriptor.h>
|
||||
#include <android_avb/avb_hashtree_descriptor.h>
|
||||
#include <android_avb/avb_kernel_cmdline_descriptor.h>
|
||||
#include <android_avb/avb_sha.h>
|
||||
#include <android_avb/avb_util.h>
|
||||
|
@ -65,10 +66,11 @@ static inline bool result_should_continue(AvbSlotVerifyResult result) {
|
|||
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) {
|
||||
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;
|
||||
|
||||
|
@ -110,9 +112,12 @@ static AvbSlotVerifyResult load_full_partition(
|
|||
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);
|
||||
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) {
|
||||
|
@ -128,6 +133,47 @@ static AvbSlotVerifyResult load_full_partition(
|
|||
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(
|
||||
AvbOps* ops,
|
||||
const char* const* requested_partitions,
|
||||
|
@ -148,6 +194,9 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
|||
size_t digest_len;
|
||||
const char* found;
|
||||
uint64_t image_size = 0;
|
||||
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(
|
||||
(const AvbHashDescriptor*)descriptor, &hash_desc)) {
|
||||
|
@ -177,15 +226,35 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
|||
goto out;
|
||||
}
|
||||
|
||||
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");
|
||||
if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) {
|
||||
/* No ab_suffix, just copy the partition name as is. */
|
||||
if (hash_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, 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;
|
||||
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
|
||||
|
@ -244,14 +313,31 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
|
|||
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(
|
||||
part_name, ": Digest in descriptor not of expected size.\n", NULL);
|
||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
|
||||
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,
|
||||
": Hash of data does not match digest in descriptor.\n",
|
||||
NULL);
|
||||
|
@ -352,7 +438,7 @@ static AvbSlotVerifyResult load_requested_partitions(
|
|||
goto out;
|
||||
}
|
||||
loaded_partition->data_size = image_size;
|
||||
loaded_partition->data = image_buf; /* Transferring the owner. */
|
||||
loaded_partition->data = image_buf; /* Transferring the owner. */
|
||||
loaded_partition->preloaded = image_preloaded;
|
||||
image_buf = NULL;
|
||||
image_preloaded = false;
|
||||
|
@ -382,7 +468,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
|||
const uint8_t* expected_public_key,
|
||||
size_t expected_public_key_length,
|
||||
AvbSlotVerifyData* slot_data,
|
||||
AvbAlgorithmType* out_algorithm_type) {
|
||||
AvbAlgorithmType* out_algorithm_type,
|
||||
AvbCmdlineSubstList* out_additional_cmdline_subst) {
|
||||
char full_partition_name[AVB_PART_NAME_MAX_SIZE];
|
||||
AvbSlotVerifyResult ret;
|
||||
AvbIOResult io_ret;
|
||||
|
@ -518,7 +605,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
|||
NULL /* expected_public_key */,
|
||||
0 /* expected_public_key_length */,
|
||||
slot_data,
|
||||
out_algorithm_type);
|
||||
out_algorithm_type,
|
||||
out_additional_cmdline_subst);
|
||||
goto out;
|
||||
} else {
|
||||
avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
|
||||
|
@ -714,7 +802,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
|||
* checks that it matches what's in the hash descriptor.
|
||||
*
|
||||
* - 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
|
||||
* image, verify vbmeta image (includes rollback checks, hash
|
||||
|
@ -785,18 +874,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
|||
sizeof(AvbChainPartitionDescriptor);
|
||||
chain_public_key = chain_partition_name + chain_desc.partition_name_len;
|
||||
|
||||
sub_ret = load_and_verify_vbmeta(ops,
|
||||
requested_partitions,
|
||||
ab_suffix,
|
||||
allow_verification_error,
|
||||
toplevel_vbmeta_flags,
|
||||
chain_desc.rollback_index_location,
|
||||
(const char*)chain_partition_name,
|
||||
chain_desc.partition_name_len,
|
||||
chain_public_key,
|
||||
chain_desc.public_key_len,
|
||||
slot_data,
|
||||
NULL /* out_algorithm_type */);
|
||||
sub_ret =
|
||||
load_and_verify_vbmeta(ops,
|
||||
requested_partitions,
|
||||
ab_suffix,
|
||||
allow_verification_error,
|
||||
toplevel_vbmeta_flags,
|
||||
chain_desc.rollback_index_location,
|
||||
(const char*)chain_partition_name,
|
||||
chain_desc.partition_name_len,
|
||||
chain_public_key,
|
||||
chain_desc.public_key_len,
|
||||
slot_data,
|
||||
NULL, /* out_algorithm_type */
|
||||
NULL /* out_additional_cmdline_subst */);
|
||||
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||
ret = sub_ret;
|
||||
if (!result_should_continue(ret)) {
|
||||
|
@ -882,9 +973,90 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
|
|||
}
|
||||
} 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_HASHTREE:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
}
|
||||
|
@ -932,6 +1104,7 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
|||
AvbVBMetaImageHeader toplevel_vbmeta;
|
||||
bool 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.
|
||||
*
|
||||
|
@ -976,6 +1149,12 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
|||
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,
|
||||
requested_partitions,
|
||||
ab_suffix,
|
||||
|
@ -987,7 +1166,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
|||
NULL /* expected_public_key */,
|
||||
0 /* expected_public_key_length */,
|
||||
slot_data,
|
||||
&algorithm_type);
|
||||
&algorithm_type,
|
||||
additional_cmdline_subst);
|
||||
if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1032,9 +1212,11 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
|||
/* Add options - any failure in avb_append_options() is either an
|
||||
* I/O or OOM error.
|
||||
*/
|
||||
AvbSlotVerifyResult sub_ret = avb_append_options(
|
||||
ops, slot_data, &toplevel_vbmeta, algorithm_type,
|
||||
hashtree_error_mode);
|
||||
AvbSlotVerifyResult sub_ret = avb_append_options(ops,
|
||||
slot_data,
|
||||
&toplevel_vbmeta,
|
||||
algorithm_type,
|
||||
hashtree_error_mode);
|
||||
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
|
||||
ret = sub_ret;
|
||||
goto fail;
|
||||
|
@ -1044,8 +1226,11 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
|
|||
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
|
||||
if (slot_data->cmdline != NULL) {
|
||||
char* new_cmdline;
|
||||
new_cmdline = avb_sub_cmdline(
|
||||
ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta);
|
||||
new_cmdline = avb_sub_cmdline(ops,
|
||||
slot_data->cmdline,
|
||||
ab_suffix,
|
||||
using_boot_for_vbmeta,
|
||||
additional_cmdline_subst);
|
||||
if (new_cmdline != slot_data->cmdline) {
|
||||
if (new_cmdline == NULL) {
|
||||
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
|
||||
|
@ -1063,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) {
|
||||
avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
|
||||
}
|
||||
|
@ -1073,6 +1261,9 @@ fail:
|
|||
if (slot_data != NULL) {
|
||||
avb_slot_verify_data_free(slot_data);
|
||||
}
|
||||
if (additional_cmdline_subst != NULL) {
|
||||
avb_free_cmdline_subst_list(additional_cmdline_subst);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1153,3 +1344,42 @@ const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
|
|||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <common.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -31,8 +32,10 @@
|
|||
|
||||
#include <android_avb/avb_sysdeps.h>
|
||||
|
||||
void abort(void);
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
|
||||
}
|
||||
int avb_memcmp(const void* src1, const void* src2, size_t n) {
|
||||
return memcmp(src1, src2, n);
|
||||
}
|
||||
|
|
|
@ -401,3 +401,30 @@ const char* avb_basename(const char* 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;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include <android_avb/avb_sysdeps.h>
|
||||
#include <android_avb/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|. */
|
||||
static void sha256(const uint8_t* data,
|
||||
uint32_t length,
|
||||
|
@ -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|. */
|
||||
static bool verify_permanent_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];
|
||||
|
||||
if (attributes->version != 1) {
|
||||
|
@ -75,10 +78,11 @@ static bool verify_permanent_attributes(
|
|||
}
|
||||
|
||||
/* Verifies the format, key version, usage, and signature of a certificate. */
|
||||
static bool verify_certificate(AvbAtxCertificate* certificate,
|
||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
uint64_t minimum_key_version,
|
||||
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]) {
|
||||
static bool verify_certificate(
|
||||
const AvbAtxCertificate* certificate,
|
||||
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
uint64_t minimum_key_version,
|
||||
const uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE]) {
|
||||
const AvbAlgorithmData* algorithm_data;
|
||||
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. */
|
||||
static bool verify_pik_certificate(AvbAtxCertificate* certificate,
|
||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
uint64_t minimum_version) {
|
||||
static bool verify_pik_certificate(
|
||||
const AvbAtxCertificate* certificate,
|
||||
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
uint64_t minimum_version) {
|
||||
uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
|
||||
|
||||
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. */
|
||||
static bool verify_psk_certificate(
|
||||
AvbAtxCertificate* certificate,
|
||||
uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
const AvbAtxCertificate* certificate,
|
||||
const uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE],
|
||||
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_usage[AVB_SHA256_DIGEST_SIZE];
|
||||
|
||||
|
@ -148,7 +153,32 @@ static bool verify_psk_certificate(
|
|||
if (0 != avb_safe_memcmp(certificate->signed_data.subject,
|
||||
expected_subject,
|
||||
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 true;
|
||||
|
@ -254,3 +284,118 @@ AvbIOResult avb_atx_validate_vbmeta_public_key(
|
|||
*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;
|
||||
return AVB_IO_RESULT_OK;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue