Merge "Rename vbmeta_mainline to vbmeta_system."
diff --git a/Android.bp b/Android.bp
index 3f43a90..7245161 100644
--- a/Android.bp
+++ b/Android.bp
@@ -74,12 +74,27 @@
],
}
-cc_prebuilt_binary {
+python_binary_host {
name: "avbtool",
- srcs: ["avbtool"],
+ srcs: [":avbtool_py"],
+ main: "avbtool.py",
required: ["fec"],
- device_supported: false,
- host_supported: true,
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+genrule {
+ name: "avbtool_py",
+ srcs: ["avbtool",],
+ out: ["avbtool.py"],
+ cmd: "cp $(in) $(out)",
}
// Build libavb - this is a static library that depends
@@ -94,7 +109,7 @@
recovery_available: true,
export_include_dirs: ["."],
target: {
- android: {
+ linux: {
srcs: ["libavb/avb_sysdeps_posix.c"],
},
linux_glibc: {
@@ -184,9 +199,11 @@
],
data: [
"avbtool",
+ "test/avbtool_signing_helper_*.py",
"test/data/*",
],
test_config: "test/libavb_host_unittest.xml",
+ test_suites: ["general-tests"],
static_libs: [
"libavb",
"libavb_ab_host",
@@ -196,6 +213,7 @@
"libgtest_host",
],
shared_libs: [
+ "libbase",
"libchrome",
"libcrypto",
],
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..a3ede88
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "postsubmit": [
+ {
+ "name": "libavb_host_unittest",
+ "host": true
+ }
+ ]
+}
diff --git a/avbtool b/avbtool
index b027bbd..610cf19 100755
--- a/avbtool
+++ b/avbtool
@@ -544,13 +544,52 @@
modulus = decode_long(modulus_blob)
exponent = 65537
- # For now, just use Crypto.PublicKey.RSA to verify the signature. This
- # is OK since 'avbtool verify_image' is not expected to run on the
- # Android builders (see bug #36809096).
- import Crypto.PublicKey.RSA
- key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
- if not key.verify(decode_long(padding_and_digest),
- (decode_long(sig_blob), None)):
+ # We used to have this:
+ #
+ # import Crypto.PublicKey.RSA
+ # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
+ # if not key.verify(decode_long(padding_and_digest),
+ # (decode_long(sig_blob), None)):
+ # return False
+ # return True
+ #
+ # but since 'avbtool verify_image' is used on the builders we don't want
+ # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
+ asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
+ '\n'
+ '[pubkeyinfo]\n'
+ 'algorithm=SEQUENCE:rsa_alg\n'
+ 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
+ '\n'
+ '[rsa_alg]\n'
+ 'algorithm=OID:rsaEncryption\n'
+ 'parameter=NULL\n'
+ '\n'
+ '[rsapubkey]\n'
+ 'n=INTEGER:%s\n'
+ 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'), hex(exponent).rstrip('L')))
+ asn1_tmpfile = tempfile.NamedTemporaryFile()
+ asn1_tmpfile.write(asn1_str)
+ asn1_tmpfile.flush()
+ der_tmpfile = tempfile.NamedTemporaryFile()
+ p = subprocess.Popen(
+ ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out', der_tmpfile.name, '-noout'])
+ retcode = p.wait()
+ if retcode != 0:
+ raise AvbError('Error generating DER file')
+
+ p = subprocess.Popen(
+ ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name, '-keyform', 'DER', '-raw'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (pout, perr) = p.communicate(str(sig_blob))
+ retcode = p.wait()
+ if retcode != 0:
+ raise AvbError('Error verifying data: {}'.format(perr))
+ recovered_data = bytearray(pout)
+ if recovered_data != padding_and_digest:
+ sys.stderr.write('Signature not correct\n')
return False
return True
@@ -1717,7 +1756,7 @@
if not value:
sys.stderr.write('No expected chain partition for partition {}. Use '
'--expected_chain_partition to specify expected '
- 'contents.\n'.
+ 'contents or --follow_chain_partitions.\n'.
format(self.partition_name))
return False
rollback_index_location, pk_blob = value
@@ -2181,15 +2220,16 @@
if num_printed == 0:
o.write(' (none)\n')
- def verify_image(self, image_filename, key_path, expected_chain_partitions):
+ def verify_image(self, image_filename, key_path, expected_chain_partitions, follow_chain_partitions):
"""Implements the 'verify_image' command.
Arguments:
image_filename: Image file to get information from (file object).
key_path: None or check that embedded public key matches key at given path.
expected_chain_partitions: List of chain partitions to check or None.
+ follow_chain_partitions: If True, will follows chain partitions even when not
+ specified with the --expected_chain_partition option
"""
-
expected_chain_partitions_map = {}
if expected_chain_partitions:
used_locations = {}
@@ -2245,26 +2285,22 @@
.format(alg_name, image.filename))
for desc in descriptors:
- if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image):
- raise AvbError('Error verifying descriptor.')
- # Note how AvbDescriptor.verify() method verifies only the descriptor
- # contents which in the case of chain descriptors means checking only its
- # contents matches what is in |expected_chain_partitions_map|.
- #
- # Specifically AvbHashtreeDescriptor.verify(), doesn't follow chain
- # descriptors e.g. if it's a chain descriptor for 'system' it will not try
- # to verify system.img. Why? Because the whole idea of chain descriptors
- # is separate organizations. That is, when they are used it's assumed that
- # all you have is the public key, not an actual image (because if you had
- # the image you wouldn't need to use a chain partition in the first
- # place).
- #
- # However in certain situations you do have the image so it would be nice
- # to add something like a --follow_chain_descriptors option for 'avbtool
- # verify_image' which will look for and follow images specified by chain
- # descriptors. Maybe it should be on a per-partition basis and specificied
- # as part of the --expected_chain_partition paramter, maybe if the
- # partition name ends with a '+' or something. Something to think about.
+ if (isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions and
+ expected_chain_partitions_map.get(desc.partition_name) == None):
+ # In this case we're processing a chain descriptor but don't have a
+ # --expect_chain_partition ... however --follow_chain_partitions was
+ # specified so we shouldn't error out in desc.verify().
+ print ('{}: Chained but ROLLBACK_SLOT (which is {}) and KEY (which has sha1 {}) not specified'
+ .format(desc.partition_name, desc.rollback_index_location,
+ hashlib.sha1(desc.public_key).hexdigest()))
+ else:
+ if not desc.verify(image_dir, image_ext, expected_chain_partitions_map, image):
+ raise AvbError('Error verifying descriptor.')
+ # Honor --follow_chain_partitions - add '--' to make the output more readable.
+ if isinstance(desc, AvbChainPartitionDescriptor) and follow_chain_partitions:
+ print '--'
+ chained_image_filename = os.path.join(image_dir, desc.partition_name + image_ext)
+ self.verify_image(chained_image_filename, key_path, None, False)
def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
@@ -4046,6 +4082,10 @@
help='Expected chain partition',
metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
action='append')
+ sub_parser.add_argument('--follow_chain_partitions',
+ help=('Follows chain partitions even when not '
+ 'specified with the --expected_chain_partition option'),
+ action='store_true')
sub_parser.set_defaults(func=self.verify_image)
sub_parser = subparsers.add_parser(
@@ -4327,7 +4367,8 @@
def verify_image(self, args):
"""Implements the 'verify_image' sub-command."""
self.avb.verify_image(args.image.name, args.key,
- args.expected_chain_partition)
+ args.expected_chain_partition,
+ args.follow_chain_partitions)
def calculate_vbmeta_digest(self, args):
"""Implements the 'calculate_vbmeta_digest' sub-command."""
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 244a652..75b26d6 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -1284,6 +1284,35 @@
return io_ret;
}
+static bool has_system_partition(AvbOps* ops, const char* ab_suffix) {
+ char part_name[AVB_PART_NAME_MAX_SIZE];
+ char* system_part_name = "system";
+ char guid_buf[37];
+ AvbIOResult io_ret;
+
+ if (!avb_str_concat(part_name,
+ sizeof part_name,
+ system_part_name,
+ avb_strlen(system_part_name),
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("System partition name and suffix does not fit.\n");
+ return false;
+ }
+
+ io_ret = ops->get_unique_guid_for_partition(
+ ops, part_name, guid_buf, sizeof guid_buf);
+ if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
+ avb_debug("No system partition.\n");
+ return false;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting unique GUID for system partition.\n");
+ return false;
+ }
+
+ return true;
+}
+
AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
const char* const* requested_partitions,
const char* ab_suffix,
@@ -1407,8 +1436,16 @@
* that the system partition is mounted.
*/
avb_assert(slot_data->cmdline == NULL);
- slot_data->cmdline =
- avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
+ // Devices with dynamic partitions won't have system partition.
+ // Instead, it has a large super partition to accommodate *.img files.
+ // See b/119551429 for details.
+ if (has_system_partition(ops, ab_suffix)) {
+ slot_data->cmdline =
+ avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
+ } else {
+ // The |cmdline| field should be a NUL-terminated string.
+ slot_data->cmdline = avb_strdup("");
+ }
if (slot_data->cmdline == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto fail;
@@ -1446,7 +1483,7 @@
}
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
- if (slot_data->cmdline != NULL) {
+ if (slot_data->cmdline != NULL && avb_strlen(slot_data->cmdline) != 0) {
char* new_cmdline;
new_cmdline = avb_sub_cmdline(ops,
slot_data->cmdline,
diff --git a/libavb_user/avb_ops_user.cpp b/libavb_user/avb_ops_user.cpp
index 3e9956f..d7815f0 100644
--- a/libavb_user/avb_ops_user.cpp
+++ b/libavb_user/avb_ops_user.cpp
@@ -39,25 +39,21 @@
#include <libavb_ab/libavb_ab.h>
+using android::fs_mgr::Fstab;
+using android::fs_mgr::GetEntryForMountPoint;
+using android::fs_mgr::ReadDefaultFstab;
+using android::fs_mgr::ReadFstabFromFile;
+
/* Open the appropriate fstab file and fallback to /fstab.device if
* that's what's being used.
*/
-static struct fstab* open_fstab(void) {
- struct fstab* fstab = fs_mgr_read_fstab_default();
-
- if (fstab != NULL) {
- return fstab;
- }
-
- fstab = fs_mgr_read_fstab("/fstab.device");
- return fstab;
+static bool open_fstab(Fstab* fstab) {
+ return ReadDefaultFstab(fstab) || ReadFstabFromFile("/fstab.device", fstab);
}
static int open_partition(const char* name, int flags) {
char* path;
int fd;
- struct fstab* fstab;
- struct fstab_rec* record;
/* Per https://android-review.googlesource.com/c/platform/system/core/+/674989
* Android now supports /dev/block/by-name/<partition_name> ... try that
@@ -93,31 +89,28 @@
* misc and then finding an entry in /dev matching the sysfs entry.
*/
- fstab = open_fstab();
- if (fstab == NULL) {
+ Fstab fstab;
+ if (!open_fstab(&fstab)) {
return -1;
}
- record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
- if (record == NULL) {
- fs_mgr_free_fstab(fstab);
+ auto record = GetEntryForMountPoint(&fstab, "/misc");
+ if (record == nullptr) {
return -1;
}
if (strcmp(name, "misc") == 0) {
- path = strdup(record->blk_device);
+ path = strdup(record->blk_device.c_str());
} else {
size_t trimmed_len, name_len;
- const char* end_slash = strrchr(record->blk_device, '/');
+ const char* end_slash = strrchr(record->blk_device.c_str(), '/');
if (end_slash == NULL) {
- fs_mgr_free_fstab(fstab);
return -1;
}
- trimmed_len = end_slash - record->blk_device + 1;
+ trimmed_len = end_slash - record->blk_device.c_str() + 1;
name_len = strlen(name);
path = static_cast<char*>(calloc(trimmed_len + name_len + 1, 1));
- strncpy(path, record->blk_device, trimmed_len);
+ strncpy(path, record->blk_device.c_str(), trimmed_len);
strncpy(path + trimmed_len, name, name_len);
}
- fs_mgr_free_fstab(fstab);
fd = open(path, flags);
free(path);
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index f63e831..e03c37e 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -48,7 +48,9 @@
void CmdlineWithHashtreeVerification(bool hashtree_verification_on);
void CmdlineWithChainedHashtreeVerification(bool hashtree_verification_on);
- void VerificationDisabled(bool use_avbctl, bool preload);
+ void VerificationDisabled(bool use_avbctl,
+ bool preload,
+ bool has_system_partition);
};
TEST_F(AvbSlotVerifyTest, Basic) {
@@ -2006,7 +2008,8 @@
}
void AvbSlotVerifyTest::VerificationDisabled(bool use_avbctl,
- bool preload_boot) {
+ bool preload_boot,
+ bool has_system_partition) {
const size_t boot_part_size = 32 * 1024 * 1024;
const size_t dtbo_part_size = 4 * 1024 * 1024;
const size_t rootfs_size = 1028 * 1024;
@@ -2089,6 +2092,10 @@
ops_.set_expected_public_key(
PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+ if (!has_system_partition) {
+ ops_.set_hidden_partitions({"system", "system_a", "system_b"});
+ }
+
// Manually set the flag the same way 'avbctl disable-verification'
// would do it.
if (use_avbctl) {
@@ -2131,8 +2138,13 @@
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
&slot_data));
EXPECT_NE(nullptr, slot_data);
- EXPECT_EQ("root=PARTUUID=1234-fake-guid-for:system_a",
- std::string(slot_data->cmdline));
+ if (has_system_partition) {
+ EXPECT_EQ("root=PARTUUID=1234-fake-guid-for:system_a",
+ std::string(slot_data->cmdline));
+ } else {
+ EXPECT_EQ("", std::string(slot_data->cmdline));
+ }
+
// Also make sure that it actually loads the boot and dtbo partitions.
EXPECT_EQ(size_t(2), slot_data->num_loaded_partitions);
EXPECT_EQ("boot",
@@ -2156,19 +2168,39 @@
}
TEST_F(AvbSlotVerifyTest, VerificationDisabledUnmodified) {
- VerificationDisabled(false, false); // use_avbctl
+ VerificationDisabled(false, // use_avbctl
+ false, // preload_boot
+ true); // has_system_partition
}
TEST_F(AvbSlotVerifyTest, VerificationDisabledModified) {
- VerificationDisabled(true, false); // use_avbctl
+ VerificationDisabled(true, // use_avbctl
+ false, // preload_boot
+ true); // has_system_partition
}
TEST_F(AvbSlotVerifyTest, VerificationDisabledUnmodifiedPreloadBoot) {
- VerificationDisabled(false, true); // use_avbctl
+ VerificationDisabled(false, // use_avbctl
+ true, // preload_boot
+ true); // has_system_partition
}
TEST_F(AvbSlotVerifyTest, VerificationDisabledModifiedPreloadBoot) {
- VerificationDisabled(true, true); // use_avbctl
+ VerificationDisabled(true, // use_avbctl
+ true, // preload_boot
+ true); // has_system_partition
+}
+
+TEST_F(AvbSlotVerifyTest, VerificationDisabledUnmodifiedNoSystemPartition) {
+ VerificationDisabled(false, // use_avbctl
+ false, // preload_boot
+ false); // has_system_partition
+}
+
+TEST_F(AvbSlotVerifyTest, VerificationDisabledModifiedNoSystemPartition) {
+ VerificationDisabled(true, // use_avbctl
+ false, // preload_boot
+ false); // has_system_partition
}
// In the event that there's no vbmeta partition, we treat the vbmeta
@@ -2827,9 +2859,9 @@
const char* kPersistentValueName = "avb.persistent_digest.factory";
// The digest for the hash descriptor which matches the factory contents.
const uint8_t kDigest[AVB_SHA256_DIGEST_SIZE] = {
- 0x18, 0x4c, 0xb3, 0x62, 0x43, 0xad, 0xb8, 0xb8, 0x7d, 0x2d, 0x8c,
- 0x48, 0x02, 0xde, 0x32, 0x12, 0x5f, 0xe2, 0x94, 0xec, 0x46, 0x75,
- 0x3d, 0x73, 0x21, 0x44, 0xee, 0x65, 0xdf, 0x68, 0xa2, 0x3d};
+ 0x2e, 0x7c, 0xab, 0x63, 0x14, 0xe9, 0x61, 0x4b, 0x6f, 0x2d, 0xa1,
+ 0x26, 0x30, 0x66, 0x1c, 0x30, 0x38, 0xe5, 0x59, 0x20, 0x25, 0xf6,
+ 0x53, 0x4b, 0xa5, 0x82, 0x3c, 0x3b, 0x34, 0x0a, 0x1c, 0xb6};
};
TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic) {
@@ -2845,7 +2877,7 @@
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1280 "
"androidboot.vbmeta.digest="
- "604268b04d4a971d2d727c79a70b2ea7f6a0e42ccbdead1983acbf015061ce6b "
+ "f7a4ce48092379fe0e913ffda10d859cd5fc19fa721c9e81f05f8bfea14b9873 "
"androidboot.vbmeta.invalidate_on_error=yes "
"androidboot.veritymode=enforcing",
last_cmdline_);
@@ -3050,7 +3082,7 @@
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1408 "
"androidboot.vbmeta.digest="
- "0bf73ed205a043d410277444a49cc5643c1046f53b3b942cdc2c16fea06acd7b "
+ "eeaa2fb8deb48b9645f817bb6a6ce05ba3ef92d0d2d9c950c2383853cd4a3064 "
"androidboot.vbmeta.invalidate_on_error=yes "
"androidboot.veritymode=enforcing",
last_cmdline_);
@@ -3077,7 +3109,7 @@
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1408 "
"androidboot.vbmeta.digest="
- "7d64315450f035f4ff93560403c46de5a7e2a0ddfc84b95bd69f7ed5654aa687 "
+ "d3f35ef7a0812d8328be7850003b2c5607b673d0aede641656c9c04fa7992d40 "
"androidboot.vbmeta.invalidate_on_error=yes "
"androidboot.veritymode=enforcing",
last_cmdline_);
@@ -3108,7 +3140,7 @@
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1408 "
"androidboot.vbmeta.digest="
- "097d002a75d1e89557b662b6db3f1ebffb8419a02e79792a97b2c4fd1c8bedc4 "
+ "d6ea8d50dce5ca6d38ea6e780bb5b5d7ee588b53a92020ad3d1c99018f3e5f52 "
"androidboot.vbmeta.invalidate_on_error=yes "
"androidboot.veritymode=enforcing",
last_cmdline_);
diff --git a/test/avb_unittest_util.cc b/test/avb_unittest_util.cc
index 59bac28..4c23a8f 100644
--- a/test/avb_unittest_util.cc
+++ b/test/avb_unittest_util.cc
@@ -24,6 +24,8 @@
#include "avb_unittest_util.h"
+#include <android-base/file.h>
+
std::string mem_to_hexstring(const uint8_t* data, size_t len) {
std::string ret;
char digits[17] = "0123456789abcdef";
@@ -42,3 +44,123 @@
size_t last = str.find_last_not_of(" \t\n");
return str.substr(first, (last - first + 1));
}
+
+namespace avb {
+
+void BaseAvbToolTest::SetUp() {
+ /* Change current directory to test executable directory so that relative path
+ * references to test dependencies don't rely on being manually run from
+ * correct directory */
+ base::SetCurrentDirectory(
+ base::FilePath(android::base::GetExecutableDirectory()));
+
+ /* Create temporary directory to stash images in. */
+ base::FilePath ret;
+ char* buf = strdup("/tmp/libavb-tests.XXXXXX");
+ ASSERT_TRUE(mkdtemp(buf) != nullptr);
+ testdir_ = base::FilePath(buf);
+ free(buf);
+
+ /* Reset memory leak tracing */
+ avb::testing_memory_reset();
+}
+
+void BaseAvbToolTest::TearDown() {
+ /* Nuke temporary directory. */
+ ASSERT_EQ(0U, testdir_.value().find("/tmp/libavb-tests"));
+ ASSERT_TRUE(base::DeleteFile(testdir_, true /* recursive */));
+ /* Ensure all memory has been freed. */
+ EXPECT_TRUE(avb::testing_memory_all_freed());
+}
+
+std::string BaseAvbToolTest::CalcVBMetaDigest(const std::string& vbmeta_image,
+ const std::string& digest_alg) {
+ base::FilePath vbmeta_path = testdir_.Append(vbmeta_image);
+ base::FilePath vbmeta_digest_path = testdir_.Append("vbmeta_digest");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
+ " --output %s",
+ vbmeta_path.value().c_str(),
+ digest_alg.c_str(),
+ vbmeta_digest_path.value().c_str());
+ std::string vbmeta_digest_data;
+ EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
+ return string_trim(vbmeta_digest_data);
+}
+
+void BaseAvbToolTest::GenerateVBMetaImage(
+ const std::string& image_name,
+ const std::string& algorithm,
+ uint64_t rollback_index,
+ const base::FilePath& key_path,
+ const std::string& additional_options) {
+ std::string signing_options;
+ if (algorithm == "") {
+ signing_options = " --algorithm NONE ";
+ } else {
+ signing_options = std::string(" --algorithm ") + algorithm + " --key " +
+ key_path.value() + " ";
+ }
+ vbmeta_image_path_ = testdir_.Append(image_name);
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image"
+ " --rollback_index %" PRIu64
+ " %s %s "
+ " --output %s",
+ rollback_index,
+ additional_options.c_str(),
+ signing_options.c_str(),
+ vbmeta_image_path_.value().c_str());
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(vbmeta_image_path_, &file_size));
+ vbmeta_image_.resize(file_size);
+ ASSERT_TRUE(base::ReadFile(vbmeta_image_path_,
+ reinterpret_cast<char*>(vbmeta_image_.data()),
+ vbmeta_image_.size()));
+}
+
+/* Generate a file with name |file_name| of size |image_size| with
+ * known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+ */
+base::FilePath BaseAvbToolTest::GenerateImage(const std::string file_name,
+ size_t image_size,
+ uint8_t start_byte) {
+ std::vector<uint8_t> image;
+ image.resize(image_size);
+ for (size_t n = 0; n < image_size; n++) {
+ image[n] = uint8_t(n + start_byte);
+ }
+ base::FilePath image_path = testdir_.Append(file_name);
+ EXPECT_EQ(image_size,
+ static_cast<const size_t>(
+ base::WriteFile(image_path,
+ reinterpret_cast<const char*>(image.data()),
+ image.size())));
+ return image_path;
+}
+
+std::string BaseAvbToolTest::InfoImage(const base::FilePath& image_path) {
+ base::FilePath tmp_path = testdir_.Append("info_output.txt");
+ EXPECT_COMMAND(0,
+ "./avbtool info_image --image %s --output %s",
+ image_path.value().c_str(),
+ tmp_path.value().c_str());
+ std::string info_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+ return info_data;
+}
+
+std::string BaseAvbToolTest::PublicKeyAVB(const base::FilePath& key_path) {
+ base::FilePath tmp_path = testdir_.Append("public_key.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool extract_public_key --key %s"
+ " --output %s",
+ key_path.value().c_str(),
+ tmp_path.value().c_str());
+ std::string key_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+ return key_data;
+}
+
+} // namespace avb
diff --git a/test/avb_unittest_util.h b/test/avb_unittest_util.h
index 014aa58..e14e6a7 100644
--- a/test/avb_unittest_util.h
+++ b/test/avb_unittest_util.h
@@ -30,7 +30,6 @@
#include <gtest/gtest.h>
#include <base/files/file_util.h>
-#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
// Encodes |len| bytes of |data| as a lower-case hex-string.
@@ -68,21 +67,7 @@
/* Calculates the vbmeta digest using 'avbtool calc_vbmeta_digest' command. */
std::string CalcVBMetaDigest(const std::string& vbmeta_image,
- const std::string& digest_alg) {
- base::FilePath vbmeta_path = testdir_.Append(vbmeta_image);
- base::FilePath vbmeta_digest_path = testdir_.Append("vbmeta_digest");
- EXPECT_COMMAND(
- 0,
- "./avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
- " --output %s",
- vbmeta_path.value().c_str(),
- digest_alg.c_str(),
- vbmeta_digest_path.value().c_str());
- std::string vbmeta_digest_data;
- EXPECT_TRUE(
- base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
- return string_trim(vbmeta_digest_data);
- }
+ const std::string& digest_alg);
/* Generates a vbmeta image, using avbtoool, with file name
* |image_name|. The generated vbmeta image will written to disk,
@@ -93,95 +78,23 @@
const std::string& algorithm,
uint64_t rollback_index,
const base::FilePath& key_path,
- const std::string& additional_options = "") {
- std::string signing_options;
- if (algorithm == "") {
- signing_options = " --algorithm NONE ";
- } else {
- signing_options = std::string(" --algorithm ") + algorithm + " --key " +
- key_path.value() + " ";
- }
- vbmeta_image_path_ = testdir_.Append(image_name);
- EXPECT_COMMAND(0,
- "./avbtool make_vbmeta_image"
- " --rollback_index %" PRIu64
- " %s %s "
- " --output %s",
- rollback_index,
- additional_options.c_str(),
- signing_options.c_str(),
- vbmeta_image_path_.value().c_str());
- int64_t file_size;
- ASSERT_TRUE(base::GetFileSize(vbmeta_image_path_, &file_size));
- vbmeta_image_.resize(file_size);
- ASSERT_TRUE(base::ReadFile(vbmeta_image_path_,
- reinterpret_cast<char*>(vbmeta_image_.data()),
- vbmeta_image_.size()));
- }
+ const std::string& additional_options = "");
/* Generate a file with name |file_name| of size |image_size| with
* known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
*/
base::FilePath GenerateImage(const std::string file_name,
size_t image_size,
- uint8_t start_byte = 0) {
- std::vector<uint8_t> image;
- image.resize(image_size);
- for (size_t n = 0; n < image_size; n++) {
- image[n] = uint8_t(n + start_byte);
- }
- base::FilePath image_path = testdir_.Append(file_name);
- EXPECT_EQ(image_size,
- static_cast<const size_t>(
- base::WriteFile(image_path,
- reinterpret_cast<const char*>(image.data()),
- image.size())));
- return image_path;
- }
+ uint8_t start_byte = 0);
/* Returns the output of 'avbtool info_image' for a given image. */
- std::string InfoImage(const base::FilePath& image_path) {
- base::FilePath tmp_path = testdir_.Append("info_output.txt");
- EXPECT_COMMAND(0,
- "./avbtool info_image --image %s --output %s",
- image_path.value().c_str(),
- tmp_path.value().c_str());
- std::string info_data;
- EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
- return info_data;
- }
+ std::string InfoImage(const base::FilePath& image_path);
/* Returns public key in AVB format for a .pem key */
- std::string PublicKeyAVB(const base::FilePath& key_path) {
- base::FilePath tmp_path = testdir_.Append("public_key.bin");
- EXPECT_COMMAND(0,
- "./avbtool extract_public_key --key %s"
- " --output %s",
- key_path.value().c_str(),
- tmp_path.value().c_str());
- std::string key_data;
- EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
- return key_data;
- }
+ std::string PublicKeyAVB(const base::FilePath& key_path);
- virtual void SetUp() override {
- /* Create temporary directory to stash images in. */
- base::FilePath ret;
- char* buf = strdup("/tmp/libavb-tests.XXXXXX");
- ASSERT_TRUE(mkdtemp(buf) != nullptr);
- testdir_ = base::FilePath(buf);
- free(buf);
- /* Reset memory leak tracing */
- avb::testing_memory_reset();
- }
-
- virtual void TearDown() override {
- /* Nuke temporary directory. */
- ASSERT_EQ(0U, testdir_.value().find("/tmp/libavb-tests"));
- ASSERT_TRUE(base::DeleteFile(testdir_, true /* recursive */));
- /* Ensure all memory has been freed. */
- EXPECT_TRUE(avb::testing_memory_all_freed());
- }
+ void SetUp() override;
+ void TearDown() override;
/* Temporary directory created in SetUp(). */
base::FilePath testdir_;
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index c436a2a..1dfdf86 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -2503,6 +2503,86 @@
pk8192_path.value().c_str());
}
+TEST_F(AvbToolTest, VerifyImageChainPartitionWithFollow) {
+ base::FilePath pk4096_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk4096_path.value().c_str());
+
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition system:1:%s ",
+ pk4096_path.value().c_str()));
+
+ const size_t system_partition_size = 10 * 1024 * 1024;
+ const size_t system_image_size = 8 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %zd --partition_name system "
+ "--algorithm SHA256_RSA4096 "
+ "--key test/data/testkey_rsa4096.pem "
+ "--internal_release_string \"\" ",
+ system_path.value().c_str(),
+ system_partition_size);
+
+ // Even without --expected_chain_partition this shouldn't fail because we use
+ // --follow_chain_partitions and system.img exists... to avoid unstable paths
+ // (e.g. /tmp/libavb.12345) in the output we need to run this from the test
+ // directory itself. It's a little ugly but it works.
+ char cwdbuf[PATH_MAX];
+ ASSERT_NE(nullptr, getcwd(cwdbuf, sizeof cwdbuf));
+ EXPECT_COMMAND(0,
+ "cd %s && (%s/avbtool verify_image "
+ "--image vbmeta.img --follow_chain_partitions > out.txt)",
+ testdir_.value().c_str(),
+ cwdbuf);
+ base::FilePath out_path = testdir_.Append("out.txt");
+ std::string out;
+ ASSERT_TRUE(base::ReadFileToString(out_path, &out));
+ EXPECT_EQ(
+ "Verifying image vbmeta.img using embedded public key\n"
+ "vbmeta: Successfully verified SHA256_RSA2048 vbmeta struct in "
+ "vbmeta.img\n"
+ "system: Chained but ROLLBACK_SLOT (which is 1) and KEY (which has sha1 "
+ "2597c218aae470a130f61162feaae70afd97f011) not specified\n"
+ "--\n"
+ "Verifying image system.img using embedded public key\n"
+ "vbmeta: Successfully verified footer and SHA256_RSA4096 vbmeta struct "
+ "in system.img\n"
+ "system: Successfully verified sha1 hashtree of system.img for image of "
+ "8388608 bytes\n",
+ out);
+
+ // Make sure we also follow partitions *even* when specifying
+ // --expect_chain_partition. The output is slightly different from above.
+ EXPECT_COMMAND(0,
+ "cd %s && (%s/avbtool verify_image "
+ "--image vbmeta.img --expected_chain_partition system:1:%s "
+ "--follow_chain_partitions > out.txt)",
+ testdir_.value().c_str(),
+ cwdbuf,
+ pk4096_path.value().c_str());
+ ASSERT_TRUE(base::ReadFileToString(out_path, &out));
+ EXPECT_EQ(
+ "Verifying image vbmeta.img using embedded public key\n"
+ "vbmeta: Successfully verified SHA256_RSA2048 vbmeta struct in "
+ "vbmeta.img\n"
+ "system: Successfully verified chain partition descriptor matches "
+ "expected data\n"
+ "--\n"
+ "Verifying image system.img using embedded public key\n"
+ "vbmeta: Successfully verified footer and SHA256_RSA4096 vbmeta struct "
+ "in system.img\n"
+ "system: Successfully verified sha1 hashtree of system.img for image of "
+ "8388608 bytes\n",
+ out);
+}
+
TEST_F(AvbToolTest, VerifyImageChainPartitionOtherVBMeta) {
base::FilePath pk4096_path = testdir_.Append("testkey_rsa4096.avbpubkey");
EXPECT_COMMAND(
diff --git a/tools/at_auth_unlock.py b/tools/at_auth_unlock.py
index f49b4a6..80b5bb6 100755
--- a/tools/at_auth_unlock.py
+++ b/tools/at_auth_unlock.py
@@ -362,7 +362,7 @@
creds.append(UnlockCredentials.from_credential_archive(path))
if verbose:
print('Found valid unlock credential bundle: ' + path)
- except (IOError, ValueError, zipfile.BadZipFile) as e:
+ except (IOError, ValueError, zipfile.BadZipfile) as e:
if verbose:
print(
"Ignoring file which isn't a valid unlock credential zip bundle: "