Rework how versioning work. am: e3cadcacd7
am: a467bedf7e

Change-Id: Icaf914c19e0aa6a45b60f380c8dc6471af719453
diff --git a/Android.mk b/Android.mk
index b6ce49f..1ac7e63 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,7 +74,8 @@
     libavb/avb_slot_verify.c \
     libavb/avb_sysdeps_posix.c \
     libavb/avb_util.c \
-    libavb/avb_vbmeta_image.c
+    libavb/avb_vbmeta_image.c \
+    libavb/avb_version.c
 include $(BUILD_STATIC_LIBRARY)
 
 # Build libavb for the host (for unit tests).
@@ -101,7 +102,8 @@
     libavb/avb_sha512.c \
     libavb/avb_slot_verify.c \
     libavb/avb_util.c \
-    libavb/avb_vbmeta_image.c
+    libavb/avb_vbmeta_image.c \
+    libavb/avb_version.c
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 # Build libavb_ab for the host (for unit tests).
diff --git a/README b/README
index 4ff6bf5..e9ffbe1 100644
--- a/README
+++ b/README
@@ -153,41 +153,59 @@
 Applications using the compiled libavb_ab library must only include
 the libavb_ab/libavb_ab.h file for similar reasons.
 
--- COMPATIBILITY NOTES
+-- VERSIONING AND COMPATIBILITY
 
-The VBMeta structure (as defined in libavb/avb_vbmeta_header.h)
-guarantees forwards- and backwards-compatibility provided the major
-version does not change.
+AVB uses a version number with three fields - the major, minor, and
+sub version. Here's an example version number
 
-When backwards-compatible changes are made - for example, when a new
-field is added to AvbVBMetaImageHeader - the minor version will be
-bumped. At the same time, libavb will also be modified to test for the
-appropriate minor version before attempting to access the newly added
-field. This ensures that version 1.N of the library is able to read an
-old vbmeta image header produced with version 1.M where N > M.
+                       1.4.3
+                       ^ ^ ^
+                       | | |
+  the major version ---+ | |
+  the minor version -----+ |
+    the sub version -------+
 
-The usual scenario is that the code parsing the AvbVBMetaImageHeader
-rarely changes (it's usually in the firmware of a device and this
-firmware is rarely updated if ever), let's say it's fixed at version
-1.N. At the same time, the version of the avbtool used to produce the
-vbmeta image is rolling forward and is at version 1.M where M > N. The
-problem with this scenario is that version 1.M may easily and
-inadvertently introduce a seemingly compatible change that isn't. For
-example, consider if a new verification algorithm is added - in this
-case version 1.N of the reference implementation will fail at
-verification time when validating the |algorithm_field| of a 1.M image
-if it's set to the new algorithm.
+The major version number is bumped only if compatibility is broken,
+e.g. a struct field has been removed or changed. The minor version
+number is bumped only if a new feature is introduced, for example a
+new algorithm or descriptor has been added. The sub version number is
+bumped when bugs are fixed or other changes not affecting
+compatibility are made.
 
-The best approach for dealing with this problem is to always used a
-pinned version of avbtool (say, use version 1.N to generate images
-targeted for devices running version 1.N) for generating and signing
-images but sometimes this is not always possible nor
-desirable. Therefore, to avoid this compatibility problem, avbtool is
-designed to always take such input as a command-line argument so it
-can be kept constant by the caller. In other words, as long as you
-keep your command-line options passed to the avb tool the same, images
-produced by newer versions of avb will continue to work on the same
-version of the reference implementation.
+The VBMeta struct (as defined in libavb/avb_vbmeta_header.h) carries
+the major and minor version number of libavb required to verify the
+struct in question. This is stored in the |required_libavb_version_major|
+and |required_libavb_version_minor| fields.
+
+Additionally the VBMeta structure contains a textual field with the
+version of avbtool used to create the struct, for example "avbtool
+1.4.3" or "avbtool 1.4.3 some_board Git-4589fbec".
+
+Note that it's entirely possible to have a VBMeta struct with
+
+ required_libavb_version_major = 1
+ required_libavb_version_minor = 0
+ avbtool_release_string = "avbtool 1.4.3"
+
+if, for example, creating an image that do not use any features added
+after AVB version 1.0.
+
+-- ADDING NEW FEATURES
+
+If adding a new feature for example a new algorithm or a new
+descriptor then AVB_VERSION_MINOR in libavb/avb_version.h and avbtool
+must be bumped and AVB_VERSION_SUB should be set to zero.
+
+Unit tests MUST be added to check that
+
+ - 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.
+
+If AVB_VERSION_MINOR has already been bumped since the last release
+there is obviously no need to bump it again.
 
 -- USAGE
 
@@ -200,7 +218,8 @@
      [--include_descriptors_from_footer /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]
+     [--signing_helper /path/to/external/signer]                                \
+     [--append_to_release_string STR]
 
 An integrity footer containing the hash for an entire partition can be
 added to an existing image as follows:
@@ -214,7 +233,8 @@
      [--include_descriptors_from_footer /path/to/image.bin]                     \
      [--setup_rootfs_from_kernel /path/to/image.bin]                            \
      [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
-     [--signing_helper /path/to/external/signer]
+     [--signing_helper /path/to/external/signer]                                \
+     [--append_to_release_string STR]
 
 An integrity footer containing the root digest and salt for a hashtree
 for a partition can be added to an existing image as follows. The
@@ -230,7 +250,8 @@
      [--setup_rootfs_from_kernel /path/to/image.bin]                            \
      [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
      [--generate_fec] [--fec_num_roots FEC_NUM_ROOTS]                           \
-     [--signing_helper /path/to/external/signer]
+     [--signing_helper /path/to/external/signer]                                \
+     [--append_to_release_string STR]
 
 The integrity footer on an image can be removed from an image. The
 hashtree can optionally be kept in place.
diff --git a/avbtool b/avbtool
index 78b87c1..9850634 100755
--- a/avbtool
+++ b/avbtool
@@ -37,11 +37,14 @@
 
 import Crypto.PublicKey.RSA
 
-# Keep in sync with avb_vbmeta_header.h.
+# Keep in sync with libavb/avb_version.h.
 AVB_VERSION_MAJOR = 1
 AVB_VERSION_MINOR = 0
+AVB_VERSION_SUB = 0
+
 AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
 
+
 class AvbError(Exception):
   """Application-specific errors.
 
@@ -173,6 +176,14 @@
 }
 
 
+def get_release_string():
+  """Calculates the release string to use in the VBMeta struct."""
+  # Keep in sync with libavb/avb_version.c:avb_version_string().
+  return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
+                                   AVB_VERSION_MINOR,
+                                   AVB_VERSION_SUB)
+
+
 def round_to_multiple(number, size):
   """Rounds a number up to nearest multiple of another number.
 
@@ -391,17 +402,17 @@
   """
   p = None
   if signing_helper is not None:
-      p = subprocess.Popen(
-          [signing_helper, algorithm_name, key_path],
-          stdin=subprocess.PIPE,
-          stdout=subprocess.PIPE,
-          stderr=subprocess.PIPE)
+    p = subprocess.Popen(
+        [signing_helper, algorithm_name, key_path],
+        stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE)
   else:
-      p = subprocess.Popen(
-          ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
-          stdin=subprocess.PIPE,
-          stdout=subprocess.PIPE,
-          stderr=subprocess.PIPE)
+    p = subprocess.Popen(
+        ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
+        stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE)
   (pout, perr) = p.communicate(str(raw_data_to_sign))
   retcode = p.wait()
   if retcode != 0:
@@ -1454,6 +1465,8 @@
   MAGIC = 'AVBf'
   SIZE = 64
   RESERVED = 28
+  FOOTER_VERSION_MAJOR = 1
+  FOOTER_VERSION_MINOR = 0
   FORMAT_STRING = ('!4s2L'  # magic, 2 x version.
                    'Q'  # Original image size.
                    'Q'  # Offset of VBMeta blob.
@@ -1480,8 +1493,8 @@
         raise LookupError('Given data does not look like a AVB footer.')
     else:
       self.magic = self.MAGIC
-      self.version_major = AVB_VERSION_MAJOR
-      self.version_minor = AVB_VERSION_MINOR
+      self.version_major = self.FOOTER_VERSION_MAJOR
+      self.version_minor = self.FOOTER_VERSION_MINOR
       self.original_image_size = 0
       self.vbmeta_offset = 0
       self.vbmeta_size = 0
@@ -1507,8 +1520,10 @@
 
   SIZE = 256
 
-  # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
-  RESERVED = 132
+  # Keep in sync with |reserved0| and |reserved| field of
+  # |AvbVBMetaImageHeader|.
+  RESERVED0 = 4
+  RESERVED = 80
 
   # Keep in sync with |AvbVBMetaImageHeader|.
   FORMAT_STRING = ('!4s2L'  # magic, 2 x version
@@ -1521,6 +1536,8 @@
                    '2Q'  # offset, size (descriptors)
                    'Q'  # rollback_index
                    'L' +  # flags
+                   str(RESERVED0) + 'x' +  # padding for reserved bytes
+                   '47sx' +  # NUL-terminated release string
                    str(RESERVED) + 'x')  # padding for reserved bytes
 
   def __init__(self, data=None):
@@ -1535,7 +1552,8 @@
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
-      (self.magic, self.header_version_major, self.header_version_minor,
+      (self.magic, self.required_libavb_version_major,
+       self.required_libavb_version_minor,
        self.authentication_data_block_size, self.auxiliary_data_block_size,
        self.algorithm_type, self.hash_offset, self.hash_size,
        self.signature_offset, self.signature_size, self.public_key_offset,
@@ -1543,14 +1561,18 @@
        self.public_key_metadata_size, self.descriptors_offset,
        self.descriptors_size,
        self.rollback_index,
-       self.flags) = struct.unpack(self.FORMAT_STRING, data)
+       self.flags,
+       self.release_string) = struct.unpack(self.FORMAT_STRING, data)
       # Nuke NUL-bytes at the end of the string.
       if self.magic != 'AVB0':
         raise AvbError('Given image does not look like a vbmeta image.')
     else:
       self.magic = 'AVB0'
-      self.header_version_major = AVB_VERSION_MAJOR
-      self.header_version_minor = AVB_VERSION_MINOR
+      # Start by just requiring version 1.0. Code that adds features
+      # in a future version can use bump_required_libavb_version_minor() to
+      # bump the minor.
+      self.required_libavb_version_major = AVB_VERSION_MAJOR
+      self.required_libavb_version_minor = 0
       self.authentication_data_block_size = 0
       self.auxiliary_data_block_size = 0
       self.algorithm_type = 0
@@ -1566,6 +1588,19 @@
       self.descriptors_size = 0
       self.rollback_index = 0
       self.flags = 0
+      self.release_string = get_release_string()
+
+  def bump_required_libavb_version_minor(self, minor):
+    """Function to bump required_libavb_version_minor.
+
+    Call this when writing data that requires a specific libavb
+    version to parse it.
+
+    Arguments:
+      minor: The minor version of libavb that has support for the feature.
+    """
+    self.required_libavb_version_minor = (
+        min(self.required_libavb_version_minor, minor))
 
   def save(self, output):
     """Serializes the header (256 bytes) to disk.
@@ -1574,14 +1609,14 @@
       output: The object to write the output to.
     """
     output.write(struct.pack(
-        self.FORMAT_STRING, self.magic, self.header_version_major,
-        self.header_version_minor, self.authentication_data_block_size,
+        self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
+        self.required_libavb_version_minor, self.authentication_data_block_size,
         self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
         self.hash_size, self.signature_offset, self.signature_size,
         self.public_key_offset, self.public_key_size,
         self.public_key_metadata_offset, self.public_key_metadata_size,
         self.descriptors_offset, self.descriptors_size, self.rollback_index,
-        self.flags))
+        self.flags, self.release_string))
 
   def encode(self):
     """Serializes the header (256) to a bytearray().
@@ -1590,14 +1625,16 @@
       A bytearray() with the encoded header.
     """
     return struct.pack(self.FORMAT_STRING, self.magic,
-                       self.header_version_major, self.header_version_minor,
+                       self.required_libavb_version_major,
+                       self.required_libavb_version_minor,
                        self.authentication_data_block_size,
                        self.auxiliary_data_block_size, self.algorithm_type,
                        self.hash_offset, self.hash_size, self.signature_offset,
                        self.signature_size, self.public_key_offset,
                        self.public_key_size, self.public_key_metadata_offset,
                        self.public_key_metadata_size, self.descriptors_offset,
-                       self.descriptors_size, self.rollback_index, self.flags)
+                       self.descriptors_size, self.rollback_index, self.flags,
+                       self.release_string)
 
 
 class Avb(object):
@@ -1719,8 +1756,9 @@
 
     (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
 
-    o.write('VBMeta image version:     {}.{}{}\n'.format(
-        header.header_version_major, header.header_version_minor,
+    o.write('Minimum libavb version:   {}.{}{}\n'.format(
+        header.required_libavb_version_major,
+        header.required_libavb_version_minor,
         ' (Sparse)' if image.is_sparse else ''))
     o.write('Header Block:             {} bytes\n'.format(AvbVBMetaHeader.SIZE))
     o.write('Authentication Block:     {} bytes\n'.format(
@@ -1730,6 +1768,8 @@
     o.write('Algorithm:                {}\n'.format(alg_name))
     o.write('Rollback Index:           {}\n'.format(header.rollback_index))
     o.write('Flags:                    {}\n'.format(header.flags))
+    o.write('Release String:           \'{}\'\n'.format(
+        header.release_string.rstrip('\0')))
 
     # Print descriptors.
     num_printed = 0
@@ -1855,7 +1895,9 @@
                         key_path, public_key_metadata_path, rollback_index,
                         flags, props, props_from_file, kernel_cmdlines,
                         setup_rootfs_from_kernel,
-                        include_descriptors_from_image, signing_helper):
+                        include_descriptors_from_image, signing_helper,
+                        release_string,
+                        append_to_release_string):
     """Implements the 'make_vbmeta_image' command.
 
     Arguments:
@@ -1872,6 +1914,8 @@
       setup_rootfs_from_kernel: None or file to generate from.
       include_descriptors_from_image: List of file objects with descriptors.
       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.
 
     Raises:
       AvbError: If a chained partition is malformed.
@@ -1898,7 +1942,8 @@
         algorithm_name, key_path, public_key_metadata_path, descriptors,
         rollback_index, flags, props, props_from_file, kernel_cmdlines,
         setup_rootfs_from_kernel,
-        include_descriptors_from_image, signing_helper)
+        include_descriptors_from_image, signing_helper, release_string,
+        append_to_release_string)
 
     # Write entire vbmeta blob (header, authentication, auxiliary).
     output.seek(0)
@@ -1909,7 +1954,8 @@
                             rollback_index, flags, props, props_from_file,
                             kernel_cmdlines,
                             setup_rootfs_from_kernel,
-                            include_descriptors_from_image, signing_helper):
+                            include_descriptors_from_image, signing_helper,
+                            release_string, append_to_release_string):
     """Generates a VBMeta blob.
 
     This blob contains the header (struct AvbVBMetaHeader), the
@@ -1935,6 +1981,8 @@
       include_descriptors_from_image: List of file objects for which
         to insert descriptors from.
       signing_helper: Program which signs a hash and return signature.
+      release_string: None or avbtool release string.
+      append_to_release_string: None or string to append.
 
     Returns:
       A bytearray() with the VBMeta blob.
@@ -2021,6 +2069,14 @@
 
     h = AvbVBMetaHeader()
 
+    # Override release string, if requested.
+    if isinstance(release_string, (str, unicode)):
+      h.release_string = release_string
+
+    # Append to release string, if requested. Also insert a space before.
+    if isinstance(append_to_release_string, (str, unicode)):
+      h.release_string += ' ' + append_to_release_string
+
     # For the Auxiliary data block, descriptors are stored at offset 0,
     # followed by the public key, followed by the public key metadata blob.
     h.auxiliary_data_block_size = round_to_multiple(
@@ -2102,6 +2158,7 @@
                       props_from_file, kernel_cmdlines,
                       setup_rootfs_from_kernel,
                       include_descriptors_from_image, signing_helper,
+                      release_string, append_to_release_string,
                       output_vbmeta_image, do_not_append_vbmeta_image):
     """Implementation of the add_hash_footer on unsparse images.
 
@@ -2123,6 +2180,8 @@
       include_descriptors_from_image: List of file objects for which
         to insert descriptors from.
       signing_helper: Program which signs a hash and return signature.
+      release_string: None or avbtool release string.
+      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.
 
@@ -2199,7 +2258,8 @@
           algorithm_name, key_path, public_key_metadata_path, [h_desc],
           rollback_index, flags, props, props_from_file, kernel_cmdlines,
           setup_rootfs_from_kernel,
-          include_descriptors_from_image, signing_helper)
+          include_descriptors_from_image, signing_helper, release_string,
+          append_to_release_string)
 
       # If the image isn't sparse, its size might not be a multiple of
       # the block size. This will screw up padding later so just grow it.
@@ -2256,6 +2316,7 @@
                           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):
     """Implements the 'add_hashtree_footer' command.
 
@@ -2286,6 +2347,8 @@
         calculate the maximum image size leaving enough room for hashtree
         and metadata with the given |partition_size|.
       signing_helper: Program which signs a hash and return signature.
+      release_string: None or avbtool release string.
+      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.
 
@@ -2424,7 +2487,8 @@
           algorithm_name, key_path, public_key_metadata_path, [ht_desc],
           rollback_index, flags, props, props_from_file, kernel_cmdlines,
           setup_rootfs_from_kernel,
-          include_descriptors_from_image, signing_helper)
+          include_descriptors_from_image, signing_helper, release_string,
+          append_to_release_string)
       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
                         len(vbmeta_blob))
       vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
@@ -2764,6 +2828,12 @@
                             help='Rollback Index',
                             type=parse_number,
                             default=0)
+    # This is used internally for unit tests. Do not include in --help output.
+    sub_parser.add_argument('--internal_release_string',
+                            help=argparse.SUPPRESS)
+    sub_parser.add_argument('--append_to_release_string',
+                            help='Text to append to release string',
+                            metavar='STR')
     sub_parser.add_argument('--prop',
                             help='Add property',
                             metavar='KEY:VALUE',
@@ -3026,7 +3096,7 @@
 
   def version(self, _):
     """Implements the 'version' sub-command."""
-    print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
+    print get_release_string()
 
   def extract_public_key(self, args):
     """Implements the 'extract_public_key' sub-command."""
@@ -3043,7 +3113,9 @@
                                args.kernel_cmdline,
                                args.setup_rootfs_from_kernel,
                                args.include_descriptors_from_image,
-                               args.signing_helper)
+                               args.signing_helper,
+                               args.internal_release_string,
+                               args.append_to_release_string)
 
   def add_hash_footer(self, args):
     """Implements the 'add_hash_footer' sub-command."""
@@ -3056,6 +3128,8 @@
                              args.setup_rootfs_from_kernel,
                              args.include_descriptors_from_image,
                              args.signing_helper,
+                             args.internal_release_string,
+                             args.append_to_release_string,
                              args.output_vbmeta_image,
                              args.do_not_append_vbmeta_image)
 
@@ -3074,10 +3148,11 @@
                                  args.setup_rootfs_from_kernel,
                                  args.include_descriptors_from_image,
                                  args.calc_max_image_size, args.signing_helper,
+                                 args.internal_release_string,
+                                 args.append_to_release_string,
                                  args.output_vbmeta_image,
                                  args.do_not_append_vbmeta_image)
 
-
   def erase_footer(self, args):
     """Implements the 'erase_footer' sub-command."""
     self.avb.erase_footer(args.image.name, args.keep_hashtree)
diff --git a/examples/uefi/Makefile b/examples/uefi/Makefile
index eae589f..6349057 100644
--- a/examples/uefi/Makefile
+++ b/examples/uefi/Makefile
@@ -54,6 +54,7 @@
     $(AVB)/libavb/avb_slot_verify.c \
     $(AVB)/libavb/avb_util.c \
     $(AVB)/libavb/avb_vbmeta_image.c \
+    $(AVB)/libavb/avb_version.c \
     $(AVB)/libavb_ab/avb_ab_flow.c
 
 EFI_OBJ_FILES   = $(notdir $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(EFI_SRC_FILES))))
diff --git a/examples/uefi/main.c b/examples/uefi/main.c
index c5a0030..8342196 100644
--- a/examples/uefi/main.c
+++ b/examples/uefi/main.c
@@ -42,7 +42,10 @@
 
   InitializeLib(ImageHandle, SystemTable);
 
-  avb_print("UEFI AVB-based bootloader\n");
+  avb_printv("UEFI AVB-based bootloader using libavb version ",
+             avb_version_string(),
+             "\n",
+             NULL);
 
   ops = uefi_avb_ops_new(ImageHandle);
   if (ops == NULL) {
@@ -68,8 +71,15 @@
   switch (ab_result) {
     case AVB_AB_FLOW_RESULT_OK:
     case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR:
-      avb_printv("slot_suffix: ", slot_data->ab_suffix, "\n", NULL);
-      avb_printv("cmdline:     ", slot_data->cmdline, "\n", NULL);
+      avb_printv("slot_suffix:    ", slot_data->ab_suffix, "\n", NULL);
+      avb_printv("cmdline:        ", slot_data->cmdline, "\n", NULL);
+      avb_printv(
+          "release string: ",
+          (const char*)((((AvbVBMetaImageHeader*)(slot_data->vbmeta_images[0]
+                                                      .vbmeta_data)))
+                            ->release_string),
+          "\n",
+          NULL);
       /* Pass 'skip_initramfs' since we're not booting into recovery
        * mode. Also pass the selected slot in androidboot.slot_suffix.
        */
diff --git a/libavb/avb_footer.c b/libavb/avb_footer.c
index 5f6ed71..b8b8211 100644
--- a/libavb/avb_footer.c
+++ b/libavb/avb_footer.c
@@ -45,7 +45,7 @@
   /* Ensure we don't attempt to access any fields if the footer major
    * version is not supported.
    */
-  if (dest->version_major > AVB_FOOTER_MAJOR_VERSION) {
+  if (dest->version_major > AVB_FOOTER_VERSION_MAJOR) {
     avb_error("No support for footer version.\n");
     return false;
   }
diff --git a/libavb/avb_footer.h b/libavb/avb_footer.h
index 6607c75..e84826f 100644
--- a/libavb/avb_footer.h
+++ b/libavb/avb_footer.h
@@ -42,9 +42,9 @@
 /* Size of the footer. */
 #define AVB_FOOTER_SIZE 64
 
-/* The current MAJOR and MINOR versions used - keep in sync with avbtool. */
-#define AVB_FOOTER_MAJOR_VERSION 1
-#define AVB_FOOTER_MINOR_VERSION 0
+/* The current footer version used - keep in sync with avbtool. */
+#define AVB_FOOTER_VERSION_MAJOR 1
+#define AVB_FOOTER_VERSION_MINOR 0
 
 /* The struct used as a footer used on partitions, used to find the
  * AvbVBMetaImageHeader struct. This struct is always stored at the
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index f3527c7..3423adc 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -30,6 +30,7 @@
 #include "avb_sha.h"
 #include "avb_util.h"
 #include "avb_vbmeta_image.h"
+#include "avb_version.h"
 
 /* Maximum allow length (in bytes) of a partition name, including
  * ab_suffix.
@@ -54,6 +55,7 @@
     case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
     case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
     case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
       return false;
 
     case AVB_SLOT_VERIFY_RESULT_OK:
@@ -367,6 +369,14 @@
                  ": Error verifying vbmeta image: invalid vbmeta header\n",
                  NULL);
       goto out;
+
+    case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+      /* No way to continue this case. */
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION;
+      avb_errorv(full_partition_name,
+                 ": Error verifying vbmeta image: unsupported AVB version\n",
+                 NULL);
+      goto out;
   }
 
   /* Byteswap the header. */
@@ -928,11 +938,11 @@
       goto fail;
     }
 
-    /* Add androidboot.vbmeta.version option. */
+    /* Add androidboot.vbmeta.avb_version option. */
     if (!cmdline_append_version(slot_data,
-                                "androidboot.vbmeta.version",
-                                AVB_MAJOR_VERSION,
-                                AVB_MINOR_VERSION)) {
+                                "androidboot.vbmeta.avb_version",
+                                AVB_VERSION_MAJOR,
+                                AVB_VERSION_MINOR)) {
       ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
       goto fail;
     }
@@ -1107,6 +1117,9 @@
     case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
       ret = "ERROR_INVALID_METADATA";
       break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
+      ret = "ERROR_UNSUPPORTED_VERSION";
+      break;
       /* Do not add a 'default:' case here because of -Wswitch. */
   }
 
diff --git a/libavb/avb_slot_verify.h b/libavb/avb_slot_verify.h
index 89cfec7..c50b847 100644
--- a/libavb/avb_slot_verify.h
+++ b/libavb/avb_slot_verify.h
@@ -49,7 +49,8 @@
   AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
   AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
   AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
-  AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA
+  AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+  AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION
 } AvbSlotVerifyResult;
 
 /* Get a textual representation of |result|. */
@@ -146,13 +147,13 @@
  *   will end up pointing to the vbmeta partition for the verified
  *   slot.
  *
- *   androidboot.vbmeta.version: This is set to the decimal value of
- *   AVB_MAJOR_VERSION followed by a dot followed by the decimal value
- *   of AVB_MINOR_VERSION, for example "1.0". This version number
- *   represents the vbmeta file format version supported by libavb
- *   copy used in the boot loader. This is not necessarily the same
- *   version number of the on-disk metadata for the slot that was
- *   verified.
+ *   androidboot.vbmeta.avb_version: This is set to the decimal value
+ *   of AVB_VERSION_MAJOR followed by a dot followed by the decimal
+ *   value of AVB_VERSION_MINOR, for example "1.0" or "1.4". This
+ *   version number represents the vbmeta file format version
+ *   supported by libavb copy used in the boot loader. This is not
+ *   necessarily the same version number of the on-disk metadata for
+ *   the slot that was verified.
  *
  * Note that androidboot.slot_suffix is not set in |cmdline| - you
  * will have to pass this command-line option yourself.
@@ -235,6 +236,10 @@
  *
  * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned if some
  * of the metadata is invalid or inconsistent.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION is returned if
+ * some of the metadata requires a newer version of libavb than what
+ * is in use.
  */
 AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
                                     const char* const* requested_partitions,
diff --git a/libavb/avb_vbmeta_image.c b/libavb/avb_vbmeta_image.c
index 61b1eda..21bbf92 100644
--- a/libavb/avb_vbmeta_image.c
+++ b/libavb/avb_vbmeta_image.c
@@ -27,6 +27,7 @@
 #include "avb_rsa.h"
 #include "avb_sha.h"
 #include "avb_util.h"
+#include "avb_version.h"
 
 AvbVBMetaVerifyResult avb_vbmeta_image_verify(
     const uint8_t* data,
@@ -67,11 +68,19 @@
   avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
                                              &h);
 
-  /* Ensure we don't attempt to access any fields if the major version
-   * is not supported.
+  /* Ensure we don't attempt to access any fields if we do not meet
+   * the specified minimum version of libavb.
    */
-  if (h.header_version_major > AVB_MAJOR_VERSION) {
-    avb_error("No support for given major version.\n");
+  if ((h.required_libavb_version_major != AVB_VERSION_MAJOR) ||
+      (h.required_libavb_version_minor > AVB_VERSION_MINOR)) {
+    avb_error("Mismatch between image version and libavb version.\n");
+    ret = AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION;
+    goto out;
+  }
+
+  /* Ensure |release_string| ends with a NUL byte. */
+  if (h.release_string[AVB_RELEASE_STRING_SIZE - 1] != '\0') {
+    avb_error("Release string does not end with a NUL byte.\n");
     goto out;
   }
 
@@ -233,8 +242,10 @@
                                                 AvbVBMetaImageHeader* dest) {
   avb_memcpy(dest, src, sizeof(AvbVBMetaImageHeader));
 
-  dest->header_version_major = avb_be32toh(dest->header_version_major);
-  dest->header_version_minor = avb_be32toh(dest->header_version_minor);
+  dest->required_libavb_version_major =
+      avb_be32toh(dest->required_libavb_version_major);
+  dest->required_libavb_version_minor =
+      avb_be32toh(dest->required_libavb_version_minor);
 
   dest->authentication_data_block_size =
       avb_be64toh(dest->authentication_data_block_size);
@@ -276,6 +287,9 @@
     case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
       ret = "INVALID_VBMETA_HEADER";
       break;
+    case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+      ret = "UNSUPPORTED_VERSION";
+      break;
     case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
       ret = "HASH_MISMATCH";
       break;
diff --git a/libavb/avb_vbmeta_image.h b/libavb/avb_vbmeta_image.h
index 832773a..0df7126 100644
--- a/libavb/avb_vbmeta_image.h
+++ b/libavb/avb_vbmeta_image.h
@@ -45,9 +45,8 @@
 #define AVB_MAGIC "AVB0"
 #define AVB_MAGIC_LEN 4
 
-/* The current MAJOR and MINOR versions used - keep in sync with avbtool. */
-#define AVB_MAJOR_VERSION 1
-#define AVB_MINOR_VERSION 0
+/* Maximum size of the release string including the terminating NUL byte. */
+#define AVB_RELEASE_STRING_SIZE 48
 
 /* Flags for the vbmeta image.
  *
@@ -103,9 +102,13 @@
  * descriptors. See avb_descriptor_foreach() for a convenience
  * function to iterate over descriptors.
  *
- * This struct is versioned, see the |header_version_major| and
- * |header_version_minor| fields. Compatibility is guaranteed only
- * within the same major version.
+ * This struct is versioned, see the |required_libavb_version_major|
+ * and |required_libavb_version_minor| fields. This represents the
+ * minimum version of libavb required to verify the header and depends
+ * on the features (e.g. algorithms, descriptors) used. Note that this
+ * may be 1.0 even if generated by an avbtool from 1.4 but where no
+ * features introduced after 1.0 has been used. See the VERSIONING AND
+ * COMPATIBILITY section in the README file for more details.
  *
  * All fields are stored in network byte order when serialized. To
  * generate a copy with fields swapped to native byte order, use the
@@ -118,10 +121,11 @@
 typedef struct AvbVBMetaImageHeader {
   /*   0: Four bytes equal to "AVB0" (AVB_MAGIC). */
   uint8_t magic[AVB_MAGIC_LEN];
-  /*   4: The major version of the vbmeta image header. */
-  uint32_t header_version_major;
-  /*   8: The minor version of the vbmeta image header. */
-  uint32_t header_version_minor;
+
+  /*   4: The major version of libavb required for this header. */
+  uint32_t required_libavb_version_major;
+  /*   8: The minor version of libavb required for this header. */
+  uint32_t required_libavb_version_minor;
 
   /*  12: The size of the signature block. */
   uint64_t authentication_data_block_size;
@@ -168,10 +172,22 @@
    */
   uint32_t flags;
 
-  /* 124: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
+  /* 124: Reserved to ensure |release_string| start on a 16-byte
+   * boundary. Must be set to zeroes.
+   */
+  uint8_t reserved0[4];
+
+  /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or
+   * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
+   * terminated. Applications must not make assumptions about how this
+   * string is formatted.
+   */
+  uint8_t release_string[AVB_RELEASE_STRING_SIZE];
+
+  /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
    * bytes. This must be set to zeroes.
    */
-  uint8_t reserved[132];
+  uint8_t reserved[80];
 } AVB_ATTR_PACKED AvbVBMetaImageHeader;
 
 /* Copies |src| to |dest|, byte-swapping fields in the process.
@@ -192,21 +208,28 @@
  * AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED is returned if the vbmeta
  * image header is valid but there is no signature or hash.
  *
- * AVB_VERIFY_INVALID_VBMETA_HEADER is returned if the header of
- * the vbmeta image is invalid, for example, invalid magic or
- * inconsistent data.
+ * AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER is returned if the
+ * header of the vbmeta image is invalid, for example, invalid magic
+ * or inconsistent data.
  *
- * AVB_VERIFY_HASH_MISMATCH is returned if the hash stored in the
- * "Authentication data" block does not match the calculated hash.
+ * AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION is returned if a) the
+ * vbmeta image requires a minimum version of libavb which exceeds the
+ * version of libavb used; or b) the vbmeta image major version
+ * differs from the major version of libavb in use.
  *
- * AVB_VERIFY_SIGNATURE_MISMATCH is returned if the signature stored
- * in the "Authentication data" block is invalid or doesn't match the
- * public key stored in the vbmeta image.
+ * AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH is returned if the hash
+ * stored in the "Authentication data" block does not match the
+ * calculated hash.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH is returned if the
+ * signature stored in the "Authentication data" block is invalid or
+ * doesn't match the public key stored in the vbmeta image.
  */
 typedef enum {
   AVB_VBMETA_VERIFY_RESULT_OK,
   AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
   AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+  AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION,
   AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
   AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH,
 } AvbVBMetaVerifyResult;
diff --git a/libavb/avb_version.c b/libavb/avb_version.c
new file mode 100644
index 0000000..31f5fa6
--- /dev/null
+++ b/libavb/avb_version.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "avb_version.h"
+
+#define AVB_QUOTE(str) #str
+#define AVB_EXPAND_AND_QUOTE(str) AVB_QUOTE(str)
+
+/* Keep in sync with get_release_string() in avbtool. */
+const char* avb_version_string(void) {
+  return AVB_EXPAND_AND_QUOTE(AVB_VERSION_MAJOR) "." AVB_EXPAND_AND_QUOTE(
+      AVB_VERSION_MINOR) "." AVB_EXPAND_AND_QUOTE(AVB_VERSION_SUB);
+}
diff --git a/libavb/avb_version.h b/libavb/avb_version.h
new file mode 100644
index 0000000..7757d09
--- /dev/null
+++ b/libavb/avb_version.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VERSION_H_
+#define AVB_VERSION_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The version number of AVB - keep in sync with avbtool. */
+#define AVB_VERSION_MAJOR 1
+#define AVB_VERSION_MINOR 0
+#define AVB_VERSION_SUB 0
+
+/* Returns a NUL-terminated string for the libavb version in use.  The
+ * returned string usually looks like "%d.%d.%d". Applications must
+ * not make assumptions about the content of this string.
+ *
+ * Boot loaders should display this string in debug/diagnostics output
+ * to aid with debugging.
+ *
+ * This is similar to the string put in the |release_string| string
+ * field in the VBMeta struct by avbtool.
+ */
+const char* avb_version_string(void);
+
+/* TODO: remove when there are no more users of AVB_{MAJOR,MINOR}_VERSION. */
+#define AVB_MAJOR_VERSION AVB_VERSION_MAJOR
+#define AVB_MINOR_VERSION AVB_VERSION_MINOR
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VERSION_H_ */
diff --git a/libavb/libavb.h b/libavb/libavb.h
index b74307c..d511584 100644
--- a/libavb/libavb.h
+++ b/libavb/libavb.h
@@ -44,6 +44,7 @@
 #include "avb_sysdeps.h"
 #include "avb_util.h"
 #include "avb_vbmeta_image.h"
+#include "avb_version.h"
 #undef AVB_INSIDE_LIBAVB_H
 
 #endif /* LIBAVB_H_ */
diff --git a/libavb_ab/avb_ab_flow.c b/libavb_ab/avb_ab_flow.c
index 006e617..3adb927 100644
--- a/libavb_ab/avb_ab_flow.c
+++ b/libavb_ab/avb_ab_flow.c
@@ -248,7 +248,8 @@
           break;
 
         case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
-          /* Even with |allow_verification_error| this is game over. */
+        case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
+          /* Even with |allow_verification_error| these mean game over. */
           set_slot_unbootable = true;
           break;
 
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 8bc6b6e..c9398a2 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -53,7 +53,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -69,7 +70,8 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
       "4161a7e655eabe16c3fe714de5d43736e7c0a190cf08d36c946d2509ce071e4d",
@@ -81,7 +83,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA512_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -97,7 +100,8 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha512 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
       "cb913d2f1a884f4e04c1db5bb181f3133fd16ac02fb367a20ef0776c0b07b3656ad1f081"
@@ -110,7 +114,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -128,7 +133,8 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=unlocked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=unlocked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
       "4161a7e655eabe16c3fe714de5d43736e7c0a190cf08d36c946d2509ce071e4d",
@@ -140,7 +146,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -161,7 +168,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   AvbSlotVerifyData* slot_data = NULL;
   const char* requested_partitions[] = {"boot", NULL};
@@ -195,7 +203,11 @@
 }
 
 TEST_F(AvbSlotVerifyTest, UnsignedVBMeta) {
-  GenerateVBMetaImage("vbmeta_a.img", "", 0, base::FilePath(""));
+  GenerateVBMetaImage("vbmeta_a.img",
+                      "",
+                      0,
+                      base::FilePath(""),
+                      "--internal_release_string \"\"");
 
   AvbSlotVerifyData* slot_data = NULL;
   const char* requested_partitions[] = {"boot", NULL};
@@ -220,7 +232,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   // Corrupt four bytes of data in the end of the image. Since the aux
   // data is at the end and this data is signed, this will change the
@@ -256,7 +269,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   // Corrupt four bytes of data in the beginning of the image. Unlike
   // the CorruptedImage test-case above (which is valid metadata) this
@@ -286,7 +300,8 @@
   GenerateVBMetaImage("vbmeta_a.img",
                       "SHA256_RSA2048",
                       42,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -339,7 +354,8 @@
       " --partition_name boot"
       " --partition_size %zd"
       " --kernel_cmdline 'cmdline in hash footer $(ANDROID_SYSTEM_PARTUUID)'"
-      " --salt deadbeef",
+      " --salt deadbeef"
+      " --internal_release_string \"\"",
       boot_path.value().c_str(),
       boot_partition_size);
 
@@ -350,17 +366,19 @@
       base::FilePath("test/data/testkey_rsa2048.pem"),
       base::StringPrintf(
           "--include_descriptors_from_image %s"
-          " --kernel_cmdline 'cmdline in vbmeta $(ANDROID_BOOT_PARTUUID)'",
+          " --kernel_cmdline 'cmdline in vbmeta $(ANDROID_BOOT_PARTUUID)'"
+          " --internal_release_string \"\"",
           boot_path.value().c_str()));
 
   EXPECT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          896 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           4\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
       "      Flags:                 0\n"
@@ -428,7 +446,8 @@
       "cmdline in vbmeta 1234-fake-guid-for:boot_a cmdline in hash footer "
       "1234-fake-guid-for:system_a "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
       "androidboot.vbmeta.digest="
       "34cdb59b955aa35d4da97701f304fabf7392eecca8c50ff1a0b7b6e1c9aaa1b8",
@@ -451,7 +470,8 @@
                  " --rollback_index 0"
                  " --partition_name boot"
                  " --partition_size %zd"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -459,7 +479,8 @@
                       "SHA256_RSA2048",
                       0,
                       base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--include_descriptors_from_image %s",
+                      base::StringPrintf("--include_descriptors_from_image %s"
+                                         " --internal_release_string \"\"",
                                          boot_path.value().c_str()));
 
   EXPECT_COMMAND(0,
@@ -522,7 +543,8 @@
                  " --partition_size %zd"
                  " --algorithm SHA256_RSA4096"
                  " --key test/data/testkey_rsa4096.pem"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -539,17 +561,19 @@
       11,
       base::FilePath("test/data/testkey_rsa2048.pem"),
       base::StringPrintf("--chain_partition boot:1:%s"
-                         " --kernel_cmdline 'cmdline2 in vbmeta'",
+                         " --kernel_cmdline 'cmdline2 in vbmeta'"
+                         " --internal_release_string \"\"",
                          pk_path.value().c_str()));
 
   EXPECT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          1728 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           11\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
       "      Partition Name:          boot\n"
@@ -568,13 +592,14 @@
       "VBMeta offset:            5242880\n"
       "VBMeta size:              2112 bytes\n"
       "--\n"
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     576 bytes\n"
       "Auxiliary Block:          1280 bytes\n"
       "Algorithm:                SHA256_RSA4096\n"
       "Rollback Index:           12\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
       "      Image Size:            5242880 bytes\n"
@@ -658,7 +683,8 @@
   EXPECT_EQ(
       "cmdline2 in hash footer cmdline2 in vbmeta "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
       "androidboot.vbmeta.digest="
       "4a45faa9adfeb94e9154fe682c11fef1a1a3d829b67cbf1a12ac7f0aa4f8e2e4",
@@ -684,7 +710,8 @@
                  " --partition_size %zd"
                  " --algorithm SHA256_RSA4096"
                  " --key test/data/testkey_rsa4096.pem"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -699,7 +726,8 @@
                       "SHA256_RSA2048",
                       0,
                       base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--chain_partition boot:1:%s",
+                      base::StringPrintf("--chain_partition boot:1:%s"
+                                         " --internal_release_string \"\"",
                                          pk_path.value().c_str()));
 
   ops_.set_expected_public_key(
@@ -759,7 +787,8 @@
                  " --partition_size %zd"
                  " --algorithm SHA256_RSA8192"
                  " --key test/data/testkey_rsa8192.pem"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -774,7 +803,8 @@
                       "SHA256_RSA2048",
                       0,
                       base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--chain_partition boot:1:%s",
+                      base::StringPrintf("--chain_partition boot:1:%s"
+                                         " --internal_release_string \"\"",
                                          pk_path.value().c_str()));
 
   ops_.set_expected_public_key(
@@ -811,7 +841,8 @@
                  " --partition_size %zd"
                  " --algorithm SHA256_RSA4096"
                  " --key test/data/testkey_rsa4096.pem"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -826,7 +857,8 @@
                       "SHA256_RSA2048",
                       110,
                       base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--chain_partition boot:1:%s",
+                      base::StringPrintf("--chain_partition boot:1:%s"
+                                         " --internal_release_string \"\"",
                                          pk_path.value().c_str()));
 
   ops_.set_expected_public_key(
@@ -894,7 +926,8 @@
                  " --partition_size %zd"
                  " --algorithm SHA256_RSA4096"
                  " --key test/data/testkey_rsa4096.pem"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  boot_path.value().c_str(),
                  boot_partition_size);
 
@@ -911,17 +944,19 @@
       11,
       base::FilePath("test/data/testkey_rsa2048.pem"),
       base::StringPrintf("--chain_partition boot:1:%s"
-                         " --kernel_cmdline 'cmdline2 in vbmeta'",
+                         " --kernel_cmdline 'cmdline2 in vbmeta'"
+                         " --internal_release_string \"\"",
                          pk_path.value().c_str()));
 
   EXPECT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          1728 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           11\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
       "      Partition Name:          boot\n"
@@ -972,7 +1007,8 @@
   EXPECT_EQ(
       "cmdline2 in hash footer cmdline2 in vbmeta "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
       "androidboot.vbmeta.digest="
       "4a45faa9adfeb94e9154fe682c11fef1a1a3d829b67cbf1a12ac7f0aa4f8e2e4",
@@ -998,7 +1034,8 @@
                  " --image %s"
                  " --partition_name foo"
                  " --partition_size %zd"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  foo_path.value().c_str(),
                  foo_partition_size);
 
@@ -1007,7 +1044,8 @@
                  " --image %s"
                  " --partition_name bar"
                  " --partition_size %zd"
-                 " --salt deadbeef",
+                 " --salt deadbeef"
+                 " --internal_release_string \"\"",
                  bar_path.value().c_str(),
                  bar_partition_size);
 
@@ -1016,18 +1054,20 @@
                       4,
                       base::FilePath("test/data/testkey_rsa2048.pem"),
                       base::StringPrintf("--include_descriptors_from_image %s"
-                                         " --include_descriptors_from_image %s",
+                                         " --include_descriptors_from_image %s"
+                                         " --internal_release_string \"\"",
                                          foo_path.value().c_str(),
                                          bar_path.value().c_str()));
 
   EXPECT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          896 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           4\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
       "      Image Size:            5242880 bytes\n"
@@ -1088,12 +1128,13 @@
 TEST_F(AvbSlotVerifyTest, PublicKeyMetadata) {
   base::FilePath md_path = GenerateImage("md.bin", 1536);
 
-  GenerateVBMetaImage(
-      "vbmeta_a.img",
-      "SHA256_RSA2048",
-      0,
-      base::FilePath("test/data/testkey_rsa2048.pem"),
-      base::StringPrintf("--public_key_metadata %s", md_path.value().c_str()));
+  GenerateVBMetaImage("vbmeta_a.img",
+                      "SHA256_RSA2048",
+                      0,
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      base::StringPrintf("--public_key_metadata %s"
+                                         " --internal_release_string \"\"",
+                                         md_path.value().c_str()));
 
   ops_.set_expected_public_key(
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
@@ -1113,7 +1154,8 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+      "androidboot.vbmeta.avb_version=1.0 "
+      "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2688 "
       "androidboot.vbmeta.digest="
       "5edcaa54f40382ee6a2fc3b86cdf383348b35ed07955e83ea32d84b69a97eaa0",
@@ -1142,7 +1184,8 @@
                  "./avbtool add_hashtree_footer --salt d00df00d --image %s "
                  "--partition_size %d --partition_name foobar "
                  "--algorithm SHA256_RSA2048 "
-                 "--key test/data/testkey_rsa2048.pem",
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\"",
                  rootfs_path.value().c_str(),
                  (int)partition_size);
 
@@ -1156,7 +1199,8 @@
       base::StringPrintf("--setup_rootfs_from_kernel %s "
                          "--kernel_cmdline should_be_in_both=1 "
                          "--algorithm SHA256_RSA2048 "
-                         "--flags %d ",
+                         "--flags %d "
+                         "--internal_release_string \"\"",
                          rootfs_path.value().c_str(),
                          hashtree_verification_on
                              ? 0
@@ -1164,13 +1208,14 @@
 
   EXPECT_EQ(
       base::StringPrintf(
-          "VBMeta image version:     1.0\n"
+          "Minimum libavb version:   1.0\n"
           "Header Block:             256 bytes\n"
           "Authentication Block:     320 bytes\n"
           "Auxiliary Block:          896 bytes\n"
           "Algorithm:                SHA256_RSA2048\n"
           "Rollback Index:           4\n"
           "Flags:                    %d\n"
+          "Release String:           ''\n"
           "Descriptors:\n"
           "    Kernel Cmdline descriptor:\n"
           "      Flags:                 1\n"
@@ -1213,7 +1258,8 @@
         "restart_on_corruption ignore_zero_blocks\" root=0xfd00 "
         "should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+        "androidboot.vbmeta.avb_version=1.0 "
+        "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
         "androidboot.vbmeta.digest="
         "32572f7a89fb44cc76e9e3adbc0cb5272d0604578b2179729aa52d2bba4061c3",
@@ -1222,7 +1268,8 @@
     EXPECT_EQ(
         "root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.version=1.0 androidboot.vbmeta.device_state=locked "
+        "androidboot.vbmeta.avb_version=1.0 "
+        "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
         "androidboot.vbmeta.digest="
         "bb630c27d09e10117092d4444ea57a99bf44101c46cf5424cfb86928a0873249",
diff --git a/test/avb_util_unittest.cc b/test/avb_util_unittest.cc
index 4ee155d..aeeb425 100644
--- a/test/avb_util_unittest.cc
+++ b/test/avb_util_unittest.cc
@@ -63,8 +63,8 @@
   n64 = 0x1122334455667788;
 
   memcpy(h.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN);
-  h.version_major = htobe32(AVB_FOOTER_MAJOR_VERSION);
-  h.version_minor = htobe32(AVB_FOOTER_MINOR_VERSION);
+  h.version_major = htobe32(AVB_FOOTER_VERSION_MAJOR);
+  h.version_minor = htobe32(AVB_FOOTER_VERSION_MINOR);
   h.original_image_size = htobe64(n64);
   n64++;
   h.vbmeta_offset = htobe64(n64);
@@ -76,8 +76,8 @@
 
   n64 = 0x1122334455667788;
 
-  EXPECT_EQ((uint32_t)AVB_FOOTER_MAJOR_VERSION, s.version_major);
-  EXPECT_EQ((uint32_t)AVB_FOOTER_MINOR_VERSION, s.version_minor);
+  EXPECT_EQ((uint32_t)AVB_FOOTER_VERSION_MAJOR, s.version_major);
+  EXPECT_EQ((uint32_t)AVB_FOOTER_VERSION_MINOR, s.version_minor);
   EXPECT_EQ(n64, s.original_image_size);
   n64++;
   EXPECT_EQ(n64, s.vbmeta_offset);
@@ -88,7 +88,7 @@
   // Check that the struct still validates if minor is bigger than
   // what we expect.
   other = h;
-  h.version_minor = htobe32(AVB_FOOTER_MINOR_VERSION + 1);
+  h.version_minor = htobe32(AVB_FOOTER_VERSION_MINOR + 1);
   EXPECT_NE(0, avb_footer_validate_and_byteswap(&other, &s));
 
   // Check for bad magic.
@@ -98,7 +98,7 @@
 
   // Check for bad major version.
   bad = h;
-  bad.version_major = htobe32(AVB_FOOTER_MAJOR_VERSION + 1);
+  bad.version_major = htobe32(AVB_FOOTER_VERSION_MAJOR + 1);
   EXPECT_EQ(0, avb_footer_validate_and_byteswap(&bad, &s));
 }
 
diff --git a/test/avb_vbmeta_image_unittest.cc b/test/avb_vbmeta_image_unittest.cc
index 560dd01..5403cd2 100644
--- a/test/avb_vbmeta_image_unittest.cc
+++ b/test/avb_vbmeta_image_unittest.cc
@@ -152,10 +152,11 @@
                       0,
                       base::FilePath("test/data/testkey_rsa2048.pem"));
 
+  // Bail if it's a different major version.
   AvbVBMetaImageHeader* h =
       reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
-  h->header_version_major = htobe32(1 + be32toh(h->header_version_major));
-  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+  h->required_libavb_version_major = htobe32(1 + AVB_VERSION_MAJOR);
+  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION,
             avb_vbmeta_image_verify(
                 vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
 }
@@ -163,10 +164,25 @@
 TEST_F(VerifyTest, MinorVersionCheck) {
   GenerateVBMetaImage("vbmeta.img", "", 0, base::FilePath(""));
 
+  // Bail if required_libavb_version_minor exceeds our libavb version.
   AvbVBMetaImageHeader* h =
       reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
-  h->header_version_minor = htobe32(1 + be32toh(h->header_version_minor));
-  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+  h->required_libavb_version_minor = htobe32(1 + AVB_VERSION_MINOR);
+  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION,
+            avb_vbmeta_image_verify(
+                vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, NulTerminatedReleaseString) {
+  GenerateVBMetaImage("vbmeta.img", "", 0, base::FilePath(""));
+
+  // Bail if |release_string| isn't NUL-terminated.
+  AvbVBMetaImageHeader* h =
+      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+  for (size_t n = 0; n < AVB_RELEASE_STRING_SIZE; n++) {
+    h->release_string[n] = 'a';
+  }
+  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
             avb_vbmeta_image_verify(
                 vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
 }
@@ -482,9 +498,9 @@
   n32 = 0x11223344;
   n64 = 0x1122334455667788;
 
-  h.header_version_major = htobe32(n32);
+  h.required_libavb_version_major = htobe32(n32);
   n32++;
-  h.header_version_minor = htobe32(n32);
+  h.required_libavb_version_minor = htobe32(n32);
   n32++;
   h.authentication_data_block_size = htobe64(n64);
   n64++;
@@ -522,9 +538,9 @@
   n32 = 0x11223344;
   n64 = 0x1122334455667788;
 
-  EXPECT_EQ(n32, s.header_version_major);
+  EXPECT_EQ(n32, s.required_libavb_version_major);
   n32++;
-  EXPECT_EQ(n32, s.header_version_minor);
+  EXPECT_EQ(n32, s.required_libavb_version_minor);
   n32++;
   EXPECT_EQ(n64, s.authentication_data_block_size);
   n64++;
@@ -560,7 +576,7 @@
   // If new fields are added, the following will fail. This is to
   // remind that byteswapping code (in avb_util.c) and unittests for
   // this should be updated.
-  static_assert(offsetof(AvbVBMetaImageHeader, reserved) == 124,
+  static_assert(offsetof(AvbVBMetaImageHeader, reserved) == 176,
                 "Remember to unittest byteswapping of newly added fields");
 }
 
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index a14bc0f..924dfbb 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -57,16 +57,77 @@
   std::string printed_version;
   ASSERT_TRUE(base::ReadFileToString(path, &printed_version));
   base::TrimWhitespaceASCII(printed_version, base::TRIM_ALL, &printed_version);
-  std::string expected_version =
-      base::StringPrintf("%d.%d", AVB_MAJOR_VERSION, AVB_MINOR_VERSION);
-  EXPECT_EQ(printed_version, expected_version);
+  // See comments in libavb/avb_version.c and avbtool's get_release_string()
+  // about being in sync.
+  EXPECT_EQ(printed_version,
+            std::string("avbtool ") + std::string(avb_version_string()));
+}
+
+TEST_F(AvbToolTest, DefaultReleaseString) {
+  GenerateVBMetaImage("vbmeta.img",
+                      "SHA256_RSA2048",
+                      0,
+                      base::FilePath("test/data/testkey_rsa2048.pem"));
+
+  // Default release string is "avbtool " + avb_version_string().
+  AvbVBMetaImageHeader h;
+  avb_vbmeta_image_header_to_host_byte_order(
+      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+  EXPECT_EQ(std::string("avbtool ") + std::string(avb_version_string()),
+            std::string((const char*)h.release_string));
+}
+
+TEST_F(AvbToolTest, ReleaseStringAppend) {
+  GenerateVBMetaImage("vbmeta.img",
+                      "SHA256_RSA2048",
+                      0,
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--append_to_release_string \"Woot XYZ\"");
+
+  // Note that avbtool inserts the space by itself.
+  std::string expected_str =
+      std::string("avbtool ") + std::string(avb_version_string()) + " Woot XYZ";
+
+  AvbVBMetaImageHeader h;
+  avb_vbmeta_image_header_to_host_byte_order(
+      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+  EXPECT_EQ(expected_str, std::string((const char*)h.release_string));
+}
+
+TEST_F(AvbToolTest, ReleaseStringAppendTruncated) {
+  // Append enough text that truncation is sure to happen.
+  std::string append_str = "0123456789abcdef0123456789abcdef0123456789abcdef";
+  std::string expected_str = std::string("avbtool ") +
+                             std::string(avb_version_string()) + " " +
+                             append_str;
+  EXPECT_GT(expected_str.size(), (size_t)(AVB_RELEASE_STRING_SIZE - 1));
+  expected_str.resize(AVB_RELEASE_STRING_SIZE - 1);
+
+  GenerateVBMetaImage(
+      "vbmeta.img",
+      "SHA256_RSA2048",
+      0,
+      base::FilePath("test/data/testkey_rsa2048.pem"),
+      std::string("--append_to_release_string \"") + append_str + "\"");
+
+  // This checks that it ends with a NUL byte.
+  EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+            avb_vbmeta_image_verify(
+                vbmeta_image_.data(), vbmeta_image_.size(), nullptr, nullptr));
+
+  // For good measure we also check here.
+  AvbVBMetaImageHeader h;
+  avb_vbmeta_image_header_to_host_byte_order(
+      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+  EXPECT_EQ(expected_str, std::string((const char*)h.release_string));
 }
 
 TEST_F(AvbToolTest, ExtractPublicKey) {
   GenerateVBMetaImage("vbmeta.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   std::string key_data =
       PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem"));
@@ -97,7 +158,8 @@
                       "--prop large_hexnumber:0xfedcba9876543210 "
                       "--prop larger_than_uint64:0xfedcba98765432101 "
                       "--prop almost_a_number:423x "
-                      "--prop_from_file blob:test/data/small_blob.bin ");
+                      "--prop_from_file blob:test/data/small_blob.bin "
+                      "--internal_release_string \"\"");
 
   AvbVBMetaImageHeader h;
   avb_vbmeta_image_header_to_host_byte_order(
@@ -194,7 +256,8 @@
   GenerateVBMetaImage("vbmeta.img",
                       "SHA256_RSA2048",
                       rollback_index,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   AvbVBMetaImageHeader h;
   avb_vbmeta_image_header_to_host_byte_order(
@@ -207,7 +270,8 @@
   GenerateVBMetaImage("vbmeta.img",
                       "SHA256_RSA2048",
                       0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"));
+                      base::FilePath("test/data/testkey_rsa2048.pem"),
+                      "--internal_release_string \"\"");
 
   const uint8_t* pubkey = NULL;
   size_t pubkey_length = 0;
@@ -243,16 +307,18 @@
                       "--prop larger_than_uint64:0xfedcba98765432101 "
                       "--prop almost_a_number:423x "
                       "--prop_from_file blob:test/data/small_blob.bin "
-                      "--prop_from_file large_blob:test/data/large_blob.bin");
+                      "--prop_from_file large_blob:test/data/large_blob.bin "
+                      "--internal_release_string \"\"");
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          3200 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Prop: foo -> 'brillo'\n"
       "    Prop: bar -> 'chromeos'\n"
@@ -321,7 +387,8 @@
                    "--partition_size %d --partition_name foobar "
                    "--algorithm SHA256_RSA2048 "
                    "--key test/data/testkey_rsa2048.pem "
-                   "--output_vbmeta %s",
+                   "--output_vbmeta %s "
+                   "--internal_release_string \"\"",
                    rootfs_path.value().c_str(),
                    (int)partition_size,
                    ext_vbmeta_path.value().c_str());
@@ -332,13 +399,14 @@
                                  "VBMeta offset:            1052672\n"
                                  "VBMeta size:              1280 bytes\n"
                                  "--\n"
-                                 "VBMeta image version:     1.0%s\n"
+                                 "Minimum libavb version:   1.0%s\n"
                                  "Header Block:             256 bytes\n"
                                  "Authentication Block:     320 bytes\n"
                                  "Auxiliary Block:          704 bytes\n"
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
+                                 "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hash descriptor:\n"
                                  "      Image Size:            1052672 bytes\n"
@@ -352,13 +420,14 @@
               InfoImage(rootfs_path));
 
     ASSERT_EQ(
-        "VBMeta image version:     1.0\n"
+        "Minimum libavb version:   1.0\n"
         "Header Block:             256 bytes\n"
         "Authentication Block:     320 bytes\n"
         "Auxiliary Block:          704 bytes\n"
         "Algorithm:                SHA256_RSA2048\n"
         "Rollback Index:           0\n"
         "Flags:                    0\n"
+        "Release String:           ''\n"
         "Descriptors:\n"
         "    Hash descriptor:\n"
         "      Image Size:            1052672 bytes\n"
@@ -407,8 +476,8 @@
   EXPECT_EQ(
       std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
       AVB_FOOTER_MAGIC);
-  EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
-  EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MAJOR, (int)f.version_major);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MINOR, (int)f.version_minor);
   EXPECT_EQ(1052672UL, f.original_image_size);
   EXPECT_EQ(1052672UL, f.vbmeta_offset);
   EXPECT_EQ(1280UL, f.vbmeta_size);
@@ -463,7 +532,8 @@
                  "--partition_size %d --partition_name foobar "
                  "--algorithm SHA256_RSA2048 "
                  "--key test/data/testkey_rsa2048.pem "
-                 "--output_vbmeta %s_2nd_run --do_not_append_vbmeta_image",
+                 "--output_vbmeta %s_2nd_run --do_not_append_vbmeta_image "
+                 "--internal_release_string \"\"",
                  rootfs_path.value().c_str(),
                  (int)partition_size,
                  ext_vbmeta_path.value().c_str());
@@ -520,7 +590,8 @@
                  "--hash_algorithm sha256 --image %s "
                  "--partition_size %d --partition_name foobar "
                  "--algorithm SHA256_RSA2048 "
-                 "--key test/data/testkey_rsa2048.pem",
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\"",
                  partition_path.value().c_str(),
                  (int)partition_size);
 
@@ -537,13 +608,14 @@
       "VBMeta offset:            10354688\n"
       "VBMeta size:              1280 bytes\n"
       "--\n"
-      "VBMeta image version:     1.0 (Sparse)\n"
+      "Minimum libavb version:   1.0 (Sparse)\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          704 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
       "      Image Size:            10354688 bytes\n"
@@ -599,7 +671,8 @@
                    "--partition_size %d --partition_name foobar "
                    "--algorithm SHA256_RSA2048 "
                    "--key test/data/testkey_rsa2048.pem "
-                   "--output_vbmeta_image %s",
+                   "--output_vbmeta_image %s "
+                   "--internal_release_string \"\"",
                    rootfs_path.value().c_str(),
                    (int)partition_size,
                    ext_vbmeta_path.value().c_str());
@@ -610,13 +683,14 @@
                                  "VBMeta offset:            1069056\n"
                                  "VBMeta size:              1344 bytes\n"
                                  "--\n"
-                                 "VBMeta image version:     1.0%s\n"
+                                 "Minimum libavb version:   1.0%s\n"
                                  "Header Block:             256 bytes\n"
                                  "Authentication Block:     320 bytes\n"
                                  "Auxiliary Block:          768 bytes\n"
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
+                                 "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hashtree descriptor:\n"
                                  "      Version of dm-verity:  1\n"
@@ -637,13 +711,14 @@
               InfoImage(rootfs_path));
 
     ASSERT_EQ(
-        "VBMeta image version:     1.0\n"
+        "Minimum libavb version:   1.0\n"
         "Header Block:             256 bytes\n"
         "Authentication Block:     320 bytes\n"
         "Auxiliary Block:          768 bytes\n"
         "Algorithm:                SHA256_RSA2048\n"
         "Rollback Index:           0\n"
         "Flags:                    0\n"
+        "Release String:           ''\n"
         "Descriptors:\n"
         "    Hashtree descriptor:\n"
         "      Version of dm-verity:  1\n"
@@ -708,8 +783,8 @@
   EXPECT_EQ(
       std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
       AVB_FOOTER_MAGIC);
-  EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
-  EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MAJOR, (int)f.version_major);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MINOR, (int)f.version_minor);
   EXPECT_EQ(1052672UL, f.original_image_size);
   EXPECT_EQ(1069056UL, f.vbmeta_offset);
   EXPECT_EQ(1344UL, f.vbmeta_size);
@@ -763,18 +838,20 @@
                  "--output %s "
                  "--setup_rootfs_from_kernel %s "
                  "--algorithm SHA256_RSA2048 "
-                 "--key test/data/testkey_rsa2048.pem",
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\"",
                  vbmeta_dmv_path.value().c_str(),
                  rootfs_path.value().c_str());
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          896 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
       "      Flags:                 1\n"
@@ -808,7 +885,8 @@
                  "--partition_size %d --partition_name foobar "
                  "--algorithm SHA256_RSA2048 "
                  "--key test/data/testkey_rsa2048.pem "
-                 "--output_vbmeta %s_2nd_run --do_not_append_vbmeta_image",
+                 "--output_vbmeta %s_2nd_run --do_not_append_vbmeta_image "
+                 "--internal_release_string \"\"",
                  rootfs_path.value().c_str(),
                  (int)partition_size,
                  ext_vbmeta_path.value().c_str());
@@ -864,7 +942,8 @@
                    "--partition_size %d --partition_name foobar "
                    "--generate_fec "
                    "--algorithm SHA256_RSA2048 "
-                   "--key test/data/testkey_rsa2048.pem",
+                   "--key test/data/testkey_rsa2048.pem "
+                   "--internal_release_string \"\"",
                    rootfs_path.value().c_str(),
                    (int)partition_size);
 
@@ -874,13 +953,14 @@
                                  "VBMeta offset:            1085440\n"
                                  "VBMeta size:              1344 bytes\n"
                                  "--\n"
-                                 "VBMeta image version:     1.0%s\n"
+                                 "Minimum libavb version:   1.0%s\n"
                                  "Header Block:             256 bytes\n"
                                  "Authentication Block:     320 bytes\n"
                                  "Auxiliary Block:          768 bytes\n"
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
+                                 "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hashtree descriptor:\n"
                                  "      Version of dm-verity:  1\n"
@@ -929,8 +1009,8 @@
   EXPECT_EQ(
       std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
       AVB_FOOTER_MAGIC);
-  EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
-  EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MAJOR, (int)f.version_major);
+  EXPECT_EQ(AVB_FOOTER_VERSION_MINOR, (int)f.version_minor);
   EXPECT_EQ(1052672UL, f.original_image_size);
   EXPECT_EQ(1085440UL, f.vbmeta_offset);
   EXPECT_EQ(1344UL, f.vbmeta_size);
@@ -986,18 +1066,20 @@
                  "--output %s "
                  "--setup_rootfs_from_kernel %s "
                  "--algorithm SHA256_RSA2048 "
-                 "--key test/data/testkey_rsa2048.pem",
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\"",
                  vbmeta_dmv_path.value().c_str(),
                  rootfs_path.value().c_str());
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          960 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
       "      Flags:                 1\n"
@@ -1061,7 +1143,8 @@
                  " --partition_size %zd"
                  " --salt deadbeef"
                  " --algorithm SHA512_RSA4096 "
-                 " --key test/data/testkey_rsa4096.pem",
+                 " --key test/data/testkey_rsa4096.pem"
+                 " --internal_release_string \"\"",
                  system_path.value().c_str(),
                  partition_size);
 }
@@ -1096,7 +1179,8 @@
                  " --salt deadbeef"
                  " --generate_fec "
                  " --algorithm SHA512_RSA4096 "
-                 " --key test/data/testkey_rsa4096.pem",
+                 " --key test/data/testkey_rsa4096.pem"
+                 " --internal_release_string \"\"",
                  system_path.value().c_str(),
                  partition_size);
 }
@@ -1111,17 +1195,19 @@
                  "--kernel_cmdline 'foo bar baz' "
                  "--kernel_cmdline 'second cmdline' "
                  "--algorithm SHA256_RSA2048 "
-                 "--key test/data/testkey_rsa2048.pem",
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\"",
                  vbmeta_path.value().c_str());
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          640 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
       "      Flags:                 0\n"
@@ -1182,14 +1268,16 @@
                  "./avbtool make_vbmeta_image "
                  "--output %s "
                  "--kernel_cmdline 'something' "
-                 "--prop name:value ",
+                 "--prop name:value "
+                 "--internal_release_string \"\"",
                  vbmeta1_path.value().c_str());
 
   EXPECT_COMMAND(0,
                  "./avbtool make_vbmeta_image "
                  "--output %s "
                  "--prop name2:value2 "
-                 "--prop name3:value3 ",
+                 "--prop name3:value3 "
+                 "--internal_release_string \"\"",
                  vbmeta2_path.value().c_str());
 
   EXPECT_COMMAND(0,
@@ -1197,19 +1285,21 @@
                  "--output %s "
                  "--prop name4:value4 "
                  "--include_descriptors_from_image %s "
-                 "--include_descriptors_from_image %s ",
+                 "--include_descriptors_from_image %s "
+                 "--internal_release_string \"\"",
                  vbmeta3_path.value().c_str(),
                  vbmeta1_path.value().c_str(),
                  vbmeta2_path.value().c_str());
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     0 bytes\n"
       "Auxiliary Block:          256 bytes\n"
       "Algorithm:                NONE\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Prop: name4 -> 'value4'\n"
       "    Prop: name -> 'value'\n"
@@ -1237,18 +1327,20 @@
       "./avbtool make_vbmeta_image "
       "--output %s "
       "--chain_partition system:1:%s "
-      "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem",
+      "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
+      "--internal_release_string \"\"",
       vbmeta_path.value().c_str(),
       pk_path.value().c_str());
 
   ASSERT_EQ(
-      "VBMeta image version:     1.0\n"
+      "Minimum libavb version:   1.0\n"
       "Header Block:             256 bytes\n"
       "Authentication Block:     320 bytes\n"
       "Auxiliary Block:          1152 bytes\n"
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
+      "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
       "      Partition Name:          system\n"
@@ -1308,7 +1400,8 @@
       "SIGNING_HELPER_TEST=\"%s\" ./avbtool make_vbmeta_image "
       "--output %s "
       "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
-      "--signing_helper test/avbtool_signing_helper_test.py",
+      "--signing_helper test/avbtool_signing_helper_test.py "
+      "--internal_release_string \"\"",
       signing_helper_test_path.value().c_str(),
       vbmeta_path.value().c_str());
 
@@ -1325,7 +1418,8 @@
       "./avbtool make_vbmeta_image "
       "--output %s "
       "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
-      "--signing_helper test/avbtool_signing_helper_test.py",
+      "--signing_helper test/avbtool_signing_helper_test.py "
+      "--internal_release_string \"\"",
       vbmeta_path.value().c_str());
 }