Define and use AftlSlotVerifyResult enum
Define a new enum for the return value of aftl_slot_verify. This allows
us to be more specific on the type of error encountered. Add an unit
test to catch the invalid signature case.
Test: atest --host libavb_host_unittest:AvbAftlVerifyTest
Bug: 153298013
Change-Id: I855b53ed444ec3b946078f29811638e69e32bb11
diff --git a/libavb_aftl/avb_aftl_verify.c b/libavb_aftl/avb_aftl_verify.c
index d1efdf9..b35e417 100644
--- a/libavb_aftl/avb_aftl_verify.c
+++ b/libavb_aftl/avb_aftl_verify.c
@@ -35,11 +35,11 @@
/* Read the vbmeta partition, after the AvbVBMetaImageHeader structure, to find
* the AftlImage.
*/
-static AvbSlotVerifyResult avb_aftl_find_aftl_image(AvbOps* ops,
- const char* part_name,
- size_t vbmeta_size,
- uint8_t* out_image_buf,
- size_t* out_image_size) {
+static AftlSlotVerifyResult avb_aftl_find_aftl_image(AvbOps* ops,
+ const char* part_name,
+ size_t vbmeta_size,
+ uint8_t* out_image_buf,
+ size_t* out_image_size) {
AvbIOResult io_ret;
avb_assert(vbmeta_size <= AVB_AFTL_MAX_AFTL_IMAGE_SIZE);
@@ -49,22 +49,28 @@
AVB_AFTL_MAX_AFTL_IMAGE_SIZE - vbmeta_size,
out_image_buf,
out_image_size);
-
- 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 AftlImage from partition.\n", NULL);
- return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ switch (io_ret) {
+ case AVB_IO_RESULT_OK:
+ break;
+ case AVB_IO_RESULT_ERROR_OOM:
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_OOM;
+ case AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION:
+ case AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION:
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND;
+ default:
+ avb_errorv(
+ part_name, ": Error loading AftlImage from partition.\n", NULL);
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_IO;
}
if (*out_image_size < 4 || (out_image_buf[0] != 'A') ||
(out_image_buf[1] != 'F') || (out_image_buf[2] != 'T') ||
(out_image_buf[3] != 'L')) {
avb_errorv(part_name, ": Unexpected AftlImage magic.\n", NULL);
- return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND;
}
- return AVB_SLOT_VERIFY_RESULT_OK;
+ return AFTL_SLOT_VERIFY_RESULT_OK;
}
/* Performs the three validation steps for an AFTL image:
@@ -72,20 +78,20 @@
2. Ensure the root hash of the Merkle tree matches that in the image.
3. Verify the signature using the transparency log public key.
*/
-static AvbSlotVerifyResult avb_aftl_verify_image(uint8_t* cur_vbmeta_data,
- size_t cur_vbmeta_size,
- uint8_t* aftl_blob,
- size_t aftl_size,
- uint8_t* key_bytes,
- size_t key_num_bytes) {
+static AftlSlotVerifyResult avb_aftl_verify_image(uint8_t* cur_vbmeta_data,
+ size_t cur_vbmeta_size,
+ uint8_t* aftl_blob,
+ size_t aftl_size,
+ uint8_t* key_bytes,
+ size_t key_num_bytes) {
size_t i;
AftlImage* image;
- AvbSlotVerifyResult result = AVB_SLOT_VERIFY_RESULT_OK;
+ AftlSlotVerifyResult result = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
/* Attempt to parse the AftlImage pointed to by aftl_blob. */
image = parse_aftl_image(aftl_blob, aftl_size);
if (!image) {
- return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_IMAGE;
}
/* Now that a valid AftlImage has been parsed, attempt to verify
@@ -96,7 +102,7 @@
if (!avb_aftl_verify_vbmeta_hash(
cur_vbmeta_data, cur_vbmeta_size, image->entries[i])) {
avb_error("AFTL vbmeta hash verification failed.\n");
- result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ result = AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH;
break;
}
/* 2. Ensure that the root hash of the Merkle tree representing
@@ -104,7 +110,7 @@
AftlIcpEntry. */
if (!avb_aftl_verify_icp_root_hash(image->entries[i])) {
avb_error("AFTL root hash verification failed.\n");
- result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ result = AFTL_SLOT_VERIFY_RESULT_ERROR_TREE_HASH_MISMATCH;
break;
}
/* 3. Verify the signature using the transparency log public
@@ -112,65 +118,65 @@
if (!avb_aftl_verify_entry_signature(
key_bytes, key_num_bytes, image->entries[i])) {
avb_error("AFTL signature verification failed on entry.\n");
- result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ result = AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE;
break;
}
+ result = AFTL_SLOT_VERIFY_RESULT_OK;
}
free_aftl_image(image);
return result;
}
-AvbSlotVerifyResult aftl_slot_verify(AvbOps* ops,
- AvbSlotVerifyData* asv_data,
- uint8_t* key_bytes,
- size_t key_size) {
+AftlSlotVerifyResult aftl_slot_verify(AvbOps* ops,
+ AvbSlotVerifyData* slot_verify_data,
+ uint8_t* key_bytes,
+ size_t key_size) {
size_t i;
size_t aftl_image_size;
size_t vbmeta_size;
uint8_t* current_aftl_blob;
char part_name[AVB_PART_NAME_MAX_SIZE];
char* pname;
- AvbSlotVerifyResult ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ AftlSlotVerifyResult ret = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
- avb_assert(asv_data != NULL);
+ avb_assert(slot_verify_data != NULL);
avb_assert(key_bytes != NULL);
avb_assert(key_size == AVB_AFTL_PUB_KEY_SIZE);
- if (asv_data->vbmeta_images == NULL) {
- return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ if (slot_verify_data->vbmeta_images == NULL) {
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
}
current_aftl_blob = avb_malloc(AVB_AFTL_MAX_AFTL_IMAGE_SIZE);
if (current_aftl_blob == NULL) {
- return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ return AFTL_SLOT_VERIFY_RESULT_ERROR_OOM;
}
/* Walk through each vbmeta blob in the AvbSlotVerifyData struct. */
- for (i = 0; i < asv_data->num_vbmeta_images; i++) {
+ for (i = 0; i < slot_verify_data->num_vbmeta_images; i++) {
/* Rebuild partition name, appending the suffix */
- pname = asv_data->vbmeta_images[i].partition_name;
+ pname = slot_verify_data->vbmeta_images[i].partition_name;
if (!avb_str_concat(part_name,
sizeof part_name,
(const char*)pname,
avb_strlen(pname),
- asv_data->ab_suffix,
- avb_strlen(asv_data->ab_suffix))) {
+ slot_verify_data->ab_suffix,
+ avb_strlen(slot_verify_data->ab_suffix))) {
avb_error("Partition name and suffix does not fit.\n");
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ ret = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
break;
}
/* Use the partition info to find the AftlImage */
- vbmeta_size = asv_data->vbmeta_images[i].vbmeta_size;
+ vbmeta_size = slot_verify_data->vbmeta_images[i].vbmeta_size;
ret = avb_aftl_find_aftl_image(
ops, part_name, vbmeta_size, current_aftl_blob, &aftl_image_size);
- if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
- avb_error("Unable to find the AftlImage.\n");
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ if (ret != AFTL_SLOT_VERIFY_RESULT_OK) {
+ avb_errorv(part_name, ": Unable to find the AftlImage.\n", NULL);
break;
}
/* Validate the AFTL image in the vbmeta image. */
- ret = avb_aftl_verify_image(asv_data->vbmeta_images[i].vbmeta_data,
+ ret = avb_aftl_verify_image(slot_verify_data->vbmeta_images[i].vbmeta_data,
vbmeta_size,
current_aftl_blob,
aftl_image_size,
diff --git a/libavb_aftl/avb_aftl_verify.h b/libavb_aftl/avb_aftl_verify.h
index fe7ef41..7c6799f 100644
--- a/libavb_aftl/avb_aftl_verify.h
+++ b/libavb_aftl/avb_aftl_verify.h
@@ -34,24 +34,71 @@
extern "C" {
#endif
-/* The entry point of AFTL validation. Takes an AvbSlotVerifyData
- struct generated by a prior call to avb_slot_verify and the transparency
- log's public key and validates the inclusion proof. It does this by
- performing the following three validation steps for an AFTL
- descriptor:
- 1. Ensure the vbmeta image hash matches that in the descriptor.
- 2. Ensure the root hash of the Merkle tree matches that in the descriptor.
- 3. Verify the signature using the transparency log public key.
- It returns AVB_SLOT_VERIFY_RESULT_OK upon success,
- AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA if at least one VBMeta did not
- have an AftlDescriptor and AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
- otherwise.
-*/
+typedef enum {
+ // When the verification succeeded.
+ AFTL_SLOT_VERIFY_RESULT_OK,
-AvbSlotVerifyResult aftl_slot_verify(AvbOps* ops,
- AvbSlotVerifyData* asv_data,
- uint8_t* key_bytes,
- size_t key_size);
+ // If at some point during the verification, a memory allocation failed. This
+ // could be the case when handling a large number of log keys or inclusion
+ // proofs.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_OOM,
+
+ // If at some point during the verification, we were not able to access some
+ // devices. This can be the case when reading the AftlImage from the
+ // partition.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_IO,
+
+ // The VBMeta hash in the inclusion proof is not matching the VBMeta image
+ // hash.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH,
+
+ // The root hash of the reconstructed tree do not match the value contained in
+ // the inclusion proof.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_TREE_HASH_MISMATCH,
+
+ // The inclusion proof signature cannot be verified by the given key.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE,
+
+ // A generic error occurred during the verification.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+
+ // At least one of the VBMetas did not have an AftlImage attached.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND,
+
+ // Some content of one of the AFTLImages was found corrupted.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_IMAGE,
+
+ // Returned if the caller passed invalid parameters, for example if the prior
+ // call to avb_slot_verify failed.
+ AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
+
+} AftlSlotVerifyResult;
+
+/* The entry point of AFTL validation. It uses the AvbSlotVerifyData structure,
+ * |slot_verify_data|, generated by a prior call to the avb_slot_verify
+ * function, and a transparency log key to validate the inclusion proof(s)
+ * attached to each VBMeta images.
+ *
+ * The caller is responsible for ensuring that the previous call to
+ * avb_slot_verify succeeded. If |slot_verify_data| is incomplete or NULL,
+ * AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT will be returned.
+ *
+ * The AftlImage structure is located after the VBMetaImage structure. Uses
+ * |ops| to read the partition where the VBMeta was loaded from.
+ *
+ * For each inclusion proof found, the following three validation steps are
+ * performed:
+ * 1. Match the VBMeta image hash with the hash in the tree leaf.
+ * 2. Match the root hash of the Merkle tree with the hash in the proof.
+ * 3. Verify the signature of the proof using the transparency log public key.
+ * See the definition of AftlSlotVerifyResult for all the possible return
+ * values.
+ */
+
+AftlSlotVerifyResult aftl_slot_verify(AvbOps* ops,
+ AvbSlotVerifyData* slot_verify_data,
+ uint8_t* key_bytes,
+ size_t key_size);
#ifdef __cplusplus
}
#endif
diff --git a/test/avb_aftl_verify_unittest.cc b/test/avb_aftl_verify_unittest.cc
index 8eedcd4..9f64294 100644
--- a/test/avb_aftl_verify_unittest.cc
+++ b/test/avb_aftl_verify_unittest.cc
@@ -125,23 +125,32 @@
};
TEST_F(AvbAftlVerifyTest, Basic) {
- AvbSlotVerifyResult result =
+ AftlSlotVerifyResult result =
aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
- EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_OK);
+ EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_OK);
}
-TEST_F(AvbAftlVerifyTest, MissingAFTLDescriptor) {
+TEST_F(AvbAftlVerifyTest, PartitionError) {
asv_test_data_->vbmeta_images[0].partition_name = (char*)"do-no-exist";
- AvbSlotVerifyResult result =
+ AftlSlotVerifyResult result =
aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
- EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA);
+ EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND);
}
-TEST_F(AvbAftlVerifyTest, NonMatchingVBMeta) {
+TEST_F(AvbAftlVerifyTest, MismatchingVBMeta) {
asv_test_data_->vbmeta_images[0].vbmeta_data[0] = 'X';
- AvbSlotVerifyResult result =
+ AftlSlotVerifyResult result =
aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
- EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION);
+ EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH);
+}
+
+TEST_F(AvbAftlVerifyTest, InvalidKey) {
+ // Corrupt the key in order to fail the verification: complement the last
+ // byte, we keep the key header valid.
+ key_bytes_[key_size_ - 1] = ~key_bytes_[key_size_ - 1];
+ AftlSlotVerifyResult result =
+ aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+ EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE);
}
} /* namespace avb */