avbtool: Add --print_required_libavb_version option.

This can be used with make_vbmeta_image, add_hash_footer, and
add_hashtree_footer to figure out what will be put in the
required_libavb_version_{major, minor} fields in the VBMeta
struct. This is useful if you also want to store this value in a file
inside system.img before adding a hashtree and integrity data to it.

Right now "1.0" will be printed out since we're still at 1.0. In the
future this may get more complicated.

As with --calc_max_image_size the caller is supposed to pass all the
same options (except --output and --image) that they would pass when
not using the option. This is because options will influence what
minimum libavb version will be required - for example, if using an
algorithm introduced post-1.0 this would be expressed with the
--algorithm option.

Bug: 62077941
Test: New unit test and all unit tests pass.
Change-Id: I97a26f5b51eb4a8c63ca7903a406b174eb88bacb
diff --git a/README.md b/README.md
index 459308a..df6218f 100644
--- a/README.md
+++ b/README.md
@@ -284,7 +284,9 @@
 * The feature is used if - and only if - suitable commands/options are
   passed to `avbtool`.
 * The `required_version_minor` field is set to the bumped value if -
-  and only if - the feature is used.
+  and only if - the feature is used. Also add tests to check that the
+  correct value is output when `--print_required_libavb_version` is
+  used.
 
 If `AVB_VERSION_MINOR` has already been bumped since the last release
 there is obviously no need to bump it again.
@@ -294,21 +296,22 @@
 The content for the vbmeta partition can be generated as follows:
 
     $ avbtool make_vbmeta_image                                                    \
-        --output OUTPUT                                                            \
+        [--output OUTPUT]                                                          \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
         [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--include_descriptors_from_image /path/to/image.bin]                      \
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
         [--chain_partition part_name:rollback_index_location:/path/to/key1.bin]    \
         [--signing_helper /path/to/external/signer]                                \
+        [--print_required_libavb_version]                                          \
         [--append_to_release_string STR]
 
 An integrity footer containing the hash for an entire partition can be
 added to an existing image as follows:
 
     $ avbtool add_hash_footer                                                      \
-        --image IMAGE                                                              \
         --partition_name PARTNAME --partition_size SIZE                            \
+        [--image IMAGE]                                                            \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
         [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--hash_algorithm HASH_ALG] [--salt HEX]                                   \
@@ -316,6 +319,7 @@
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
         [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
         [--signing_helper /path/to/external/signer]                                \
+        [--print_required_libavb_version]                                          \
         [--append_to_release_string STR]                                           \
         [--calc_max_image_size]
 
@@ -324,8 +328,8 @@
 hashtree is also appended to the image.
 
     $ avbtool add_hashtree_footer                                                  \
-        --image IMAGE                                                              \
         --partition_name PARTNAME --partition_size SIZE                            \
+        [--image IMAGE]                                                            \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
         [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--hash_algorithm HASH_ALG] [--salt HEX] [--block_size SIZE]               \
@@ -335,6 +339,7 @@
         [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
         [--do_not_generate_fec] [--fec_num_roots FEC_NUM_ROOTS]                    \
         [--signing_helper /path/to/external/signer]                                \
+        [--print_required_libavb_version]                                          \
         [--append_to_release_string STR]                                           \
         [--calc_max_image_size]
 
@@ -368,6 +373,18 @@
         --calc_max_image_size
     10330112
 
+To calculate the required libavb version that would be put in the
+vbmeta struct when using `make_vbmeta_image`, `add_hash_footer`, and
+`add_hashtree_footer` commands use the
+`--print_required_libavb_version` option:
+
+    $ avbtool make_vbmeta_image \
+        --algorithm SHA256_RSA2048 --key /path/to/key.pem \
+        --include_descriptors_from_image /path/to/boot.img \
+        --include_descriptors_from_image /path/to/system.img \
+        --print_required_libavb_version
+    1.0
+
 The `--signing_helper` option can be used in `make_vbmeta_image`,
 `add_hash_footer` and `add_hashtree_footer` commands to specify any
 external program for signing hashes. The data to sign (including
diff --git a/avbtool b/avbtool
index 5df31b4..75bae27 100755
--- a/avbtool
+++ b/avbtool
@@ -2336,7 +2336,8 @@
                         setup_rootfs_from_kernel,
                         include_descriptors_from_image, signing_helper,
                         release_string,
-                        append_to_release_string):
+                        append_to_release_string,
+                        print_required_libavb_version):
     """Implements the 'make_vbmeta_image' command.
 
     Arguments:
@@ -2355,11 +2356,22 @@
       signing_helper: Program which signs a hash and return signature.
       release_string: None or avbtool release string to use instead of default.
       append_to_release_string: None or string to append.
+      print_required_libavb_version: True to only print required libavb version.
 
     Raises:
       AvbError: If a chained partition is malformed.
     """
 
+    # If we're asked to calculate minimum required libavb version, we're done.
+    #
+    # NOTE: When we get to 1.1 and later this will get more complicated.
+    if print_required_libavb_version:
+      print '1.0'
+      return
+
+    if not output:
+      raise AvbError('No output file given')
+
     descriptors = []
     ht_desc_to_setup = None
     vbmeta_blob = self._generate_vbmeta_blob(
@@ -2694,7 +2706,8 @@
                       setup_rootfs_from_kernel,
                       include_descriptors_from_image, calc_max_image_size,
                       signing_helper, release_string, append_to_release_string,
-                      output_vbmeta_image, do_not_append_vbmeta_image):
+                      output_vbmeta_image, do_not_append_vbmeta_image,
+                      print_required_libavb_version):
     """Implementation of the add_hash_footer on unsparse images.
 
     Arguments:
@@ -2724,10 +2737,19 @@
       append_to_release_string: None or string to append.
       output_vbmeta_image: If not None, also write vbmeta struct to this file.
       do_not_append_vbmeta_image: If True, don't append vbmeta struct.
+      print_required_libavb_version: True to only print required libavb version.
 
     Raises:
       AvbError: If an argument is incorrect.
     """
+
+    # If we're asked to calculate minimum required libavb version, we're done.
+    #
+    # NOTE: When we get to 1.1 and later this will get more complicated.
+    if print_required_libavb_version:
+      print '1.0'
+      return
+
     # First, calculate the maximum image size such that an image
     # this size + metadata (footer + vbmeta struct) fits in
     # |partition_size|.
@@ -2862,7 +2884,8 @@
                           include_descriptors_from_image,
                           calc_max_image_size, signing_helper,
                           release_string, append_to_release_string,
-                          output_vbmeta_image, do_not_append_vbmeta_image):
+                          output_vbmeta_image, do_not_append_vbmeta_image,
+                          print_required_libavb_version):
     """Implements the 'add_hashtree_footer' command.
 
     See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
@@ -2900,10 +2923,19 @@
       append_to_release_string: None or string to append.
       output_vbmeta_image: If not None, also write vbmeta struct to this file.
       do_not_append_vbmeta_image: If True, don't append vbmeta struct.
+      print_required_libavb_version: True to only print required libavb version.
 
     Raises:
       AvbError: If an argument is incorrect.
     """
+
+    # If we're asked to calculate minimum required libavb version, we're done.
+    #
+    # NOTE: When we get to 1.1 and later this will get more complicated.
+    if print_required_libavb_version:
+      print '1.0'
+      return
+
     digest_size = len(hashlib.new(name=hash_algorithm).digest())
     digest_padding = round_to_pow2(digest_size) - digest_size
 
@@ -3404,6 +3436,11 @@
                             metavar='IMAGE',
                             action='append',
                             type=argparse.FileType('rb'))
+    sub_parser.add_argument('--print_required_libavb_version',
+                            help=('Don\'t store the footer - '
+                                  'instead calculate the required libavb '
+                                  'version for the given options.'),
+                            action='store_true')
     # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
     sub_parser.add_argument('--chain_partition',
                             help='Allow signed integrity-data for partition',
@@ -3458,8 +3495,7 @@
                                        help='Makes a vbmeta image.')
     sub_parser.add_argument('--output',
                             help='Output file name',
-                            type=argparse.FileType('wb'),
-                            required=True)
+                            type=argparse.FileType('wb'))
     self._add_common_args(sub_parser)
     sub_parser.set_defaults(func=self.make_vbmeta_image)
 
@@ -3470,8 +3506,7 @@
                             type=argparse.FileType('rab+'))
     sub_parser.add_argument('--partition_size',
                             help='Partition size',
-                            type=parse_number,
-                            required=True)
+                            type=parse_number)
     sub_parser.add_argument('--partition_name',
                             help='Partition name',
                             default=None)
@@ -3517,8 +3552,7 @@
                             type=argparse.FileType('rab+'))
     sub_parser.add_argument('--partition_size',
                             help='Partition size',
-                            type=parse_number,
-                            required=True)
+                            type=parse_number)
     sub_parser.add_argument('--partition_name',
                             help='Partition name',
                             default=None)
@@ -3731,7 +3765,8 @@
                                args.include_descriptors_from_image,
                                args.signing_helper,
                                args.internal_release_string,
-                               args.append_to_release_string)
+                               args.append_to_release_string,
+                               args.print_required_libavb_version)
 
   def append_vbmeta_image(self, args):
     """Implements the 'append_vbmeta_image' sub-command."""
@@ -3755,7 +3790,8 @@
                              args.internal_release_string,
                              args.append_to_release_string,
                              args.output_vbmeta_image,
-                             args.do_not_append_vbmeta_image)
+                             args.do_not_append_vbmeta_image,
+                             args.print_required_libavb_version)
 
   def add_hashtree_footer(self, args):
     """Implements the 'add_hashtree_footer' sub-command."""
@@ -3783,7 +3819,8 @@
                                  args.internal_release_string,
                                  args.append_to_release_string,
                                  args.output_vbmeta_image,
-                                 args.do_not_append_vbmeta_image)
+                                 args.do_not_append_vbmeta_image,
+                                 args.print_required_libavb_version)
 
   def erase_footer(self, args):
     """Implements the 'erase_footer' sub-command."""
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index f1c1831..e8470d7 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -1849,6 +1849,44 @@
                  pk8192_path.value().c_str());
 }
 
+TEST_F(AvbToolTest, PrintRequiredLibavbVersion) {
+  base::FilePath output_path = testdir_.Append("versions.txt");
+
+  const size_t boot_partition_size = 16 * 1024 * 1024;
+  EXPECT_COMMAND(0,
+                 "./avbtool add_hash_footer"
+                 " --rollback_index 0"
+                 " --partition_name boot"
+                 " --partition_size %zd"
+                 " --salt deadbeef"
+                 " --internal_release_string \"\""
+                 " --print_required_libavb_version >> %s",
+                 boot_partition_size,
+                 output_path.value().c_str());
+
+  const size_t system_partition_size = 10 * 1024 * 1024;
+  EXPECT_COMMAND(0,
+                 "./avbtool add_hashtree_footer --salt d00df00d "
+                 "--partition_size %zd --partition_name system "
+                 "--internal_release_string \"\""
+                 " --print_required_libavb_version >> %s",
+                 system_partition_size,
+                 output_path.value().c_str());
+
+  EXPECT_COMMAND(0,
+                 "./avbtool make_vbmeta_image "
+                 "--algorithm SHA256_RSA2048 "
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\""
+                 " --print_required_libavb_version >> %s",
+                 output_path.value().c_str());
+
+  // Check that "1.0\n" was printed for all three invocations.
+  std::string versions;
+  ASSERT_TRUE(base::ReadFileToString(output_path, &versions));
+  EXPECT_EQ(versions, std::string("1.0\n1.0\n1.0\n"));
+}
+
 TEST_F(AvbToolTest, MakeAtxPikCertificate) {
   base::FilePath subject_path = testdir_.Append("tmp_subject");
   ASSERT_TRUE(base::WriteFile(subject_path, "fake PIK subject", 16));