Snap for 5735642 from 81dd5055b1e1b1891e26e08c94817d05ccf6c44a to sdk-release

Change-Id: I7718e66446664327d69bd1dd6e79ae46a3936060
diff --git a/README.md b/README.md
index 0b1ec97..f64c6de 100644
--- a/README.md
+++ b/README.md
@@ -164,6 +164,24 @@
 known good operating systems which, if found, provides additional assurance
 about the device the application is running on.
 
+For [factory images of Pixel 3 and later
+devices](https://developers.google.com/android/images), the
+`pixel_factory_image_verify.py` located in `tools/transparency` is a convenience
+tool for downloading, verifying and calcuating VBMeta Digests.
+
+    $ pixel_factory_image_verify.py https://dl.google.com/dl/android/aosp/image.zip
+    Fetching file from: https://dl.google.com/dl/android/aosp/image.zip
+    Successfully downloaded file.
+    Successfully unpacked factory image.
+    Successfully unpacked factory image partitions.
+    Successfully verified VBmeta.
+    Successfully calculated VBMeta Digest.
+    The VBMeta Digest for factory image is: 1f329b20a2dd69425e7a29566ca870dad51d2c579311992d41c9ba9ba05e170e
+
+If the given argument is not an URL it considered to be a local file:
+
+    $ pixel_factory_image_verify.py image.zip
+
 # Tools and Libraries
 
 This section contains information about the tools and libraries
@@ -576,8 +594,8 @@
 `system.img`, a kernel-cmdline descriptor for setting up `dm-verity`
 for `system.img` and append a hash-tree to `system.img`. If the build
 system is set up such that one or many of `vendor.img` / `product.img`
-/ `odm.img` / `product_services.img` are being built, the hash-tree for
-each of them will also be appended to the image respectively, and their
+/ `system_ext.img` / `odm.img` are being built, the hash-tree for each
+of them will also be appended to the image respectively, and their
 hash-tree descriptors will be included into `vbmeta.img` accordingly.
 
 By default, the algorithm `SHA256_RSA4096` is used with a test key
@@ -605,20 +623,20 @@
 partitions without changing the top-level `vbmeta` partition. For example,
 the following variables create `vbmeta_system.img` as a chained `vbmeta`
 image that contains the hash-tree descriptors for `system.img` and
-`product_services.img`. `vbmeta_system.img` itself will be signed by the
-specified key and algorithm.
+`system_ext.img`. `vbmeta_system.img` itself will be signed by the specified
+key and algorithm.
 
-    BOARD_AVB_VBMETA_SYSTEM := system product_services
+    BOARD_AVB_VBMETA_SYSTEM := system system_ext
     BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
     BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
     BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
 
-Note that the hash-tree descriptors for `system.img` and
-`product_services.img` will be included only in `vbmeta_system.img`, but
-not `vbmeta.img`. With the above setup, partitions `system.img`,
-`product_services.img` and `vbmeta_system.img` can be updated
-independently - but as a group - of the rest of the partitions, *or* as
-part of the traditional updates that update all the partitions.
+Note that the hash-tree descriptors for `system.img` and `system_ext.img`
+will be included only in `vbmeta_system.img`, but not `vbmeta.img`. With
+the above setup, partitions `system.img`, `system_ext.img` and
+`vbmeta_system.img` can be updated independently - but as a group - of the
+rest of the partitions, *or* as part of the traditional updates that
+update all the partitions.
 
 Currently build system supports building chained `vbmeta` images of
 `vbmeta_system.img` (`BOARD_AVB_VBMETA_SYSTEM`) and `vbmeta_vendor.img`
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index eb474ec..9124b24 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -584,7 +584,7 @@
   size_t num_descriptors;
   size_t n;
   bool is_main_vbmeta;
-  bool is_vbmeta_partition;
+  bool look_for_vbmeta_footer;
   AvbVBMetaData* vbmeta_image_data = NULL;
 
   ret = AVB_SLOT_VERIFY_RESULT_OK;
@@ -596,7 +596,14 @@
    * vbmeta struct.
    */
   is_main_vbmeta = (rollback_index_location == 0);
-  is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
+
+  /* Don't use footers for vbmeta partitions ('vbmeta' or
+   * 'vbmeta_<partition_name>').
+   */
+  look_for_vbmeta_footer = true;
+  if (avb_strncmp(partition_name, "vbmeta", avb_strlen("vbmeta")) == 0) {
+    look_for_vbmeta_footer = false;
+  }
 
   if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
     avb_error("Partition name is not valid UTF-8.\n");
@@ -624,7 +631,7 @@
    */
   vbmeta_offset = 0;
   vbmeta_size = VBMETA_MAX_SIZE;
-  if (!is_vbmeta_partition) {
+  if (look_for_vbmeta_footer) {
     uint8_t footer_buf[AVB_FOOTER_SIZE];
     size_t footer_num_read;
     AvbFooter footer;
@@ -692,7 +699,7 @@
      * go try to get it from the boot partition instead.
      */
     if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
-        is_vbmeta_partition) {
+        !look_for_vbmeta_footer) {
       avb_debugv(full_partition_name,
                  ": No such partition. Trying 'boot' instead.\n",
                  NULL);
diff --git a/libavb/avb_sysdeps.h b/libavb/avb_sysdeps.h
index e1f2ebc..e511a8a 100644
--- a/libavb/avb_sysdeps.h
+++ b/libavb/avb_sysdeps.h
@@ -75,6 +75,14 @@
  */
 int avb_strcmp(const char* s1, const char* s2);
 
+/* Compare |n| bytes in two strings.
+ *
+ * Return an integer less than, equal to, or greater than zero if the
+ * first |n| bytes of |s1| is found, respectively, to be less than,
+ * to match, or be greater than the first |n| bytes of |s2|.
+ */
+int avb_strncmp(const char* s1, const char* s2, size_t n);
+
 /* Copy |n| bytes from |src| to |dest|. */
 void* avb_memcpy(void* dest, const void* src, size_t n);
 
diff --git a/libavb/avb_sysdeps_posix.c b/libavb/avb_sysdeps_posix.c
index 0cbabee..e26c3ef 100644
--- a/libavb/avb_sysdeps_posix.c
+++ b/libavb/avb_sysdeps_posix.c
@@ -46,6 +46,10 @@
   return strcmp(s1, s2);
 }
 
+int avb_strncmp(const char* s1, const char* s2, size_t n) {
+  return strncmp(s1, s2, n);
+}
+
 size_t avb_strlen(const char* str) {
   return strlen(str);
 }
diff --git a/test/avb_sysdeps_posix_testing.cc b/test/avb_sysdeps_posix_testing.cc
index ee1cb97..0a6ac13 100644
--- a/test/avb_sysdeps_posix_testing.cc
+++ b/test/avb_sysdeps_posix_testing.cc
@@ -50,6 +50,10 @@
   return strcmp(s1, s2);
 }
 
+int avb_strncmp(const char* s1, const char* s2, size_t n) {
+  return strncmp(s1, s2, n);
+}
+
 size_t avb_strlen(const char* str) {
   return strlen(str);
 }
diff --git a/tools/transparency/pixel_factory_image_verify.py b/tools/transparency/pixel_factory_image_verify.py
new file mode 100755
index 0000000..f837fad
--- /dev/null
+++ b/tools/transparency/pixel_factory_image_verify.py
@@ -0,0 +1,308 @@
+#!/usr/bin/env python
+
+# Copyright 2019, 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.
+#
+
+"""Tool for verifying VBMeta & calculate VBMeta Digests of Pixel factory images.
+
+If given an HTTPS URL it will download the file first before processing.
+$ pixel_factory_image_verify.py https://dl.google.com/dl/android/aosp/image.zip
+
+Otherwise, the argument is considered to be a local file.
+$ pixel_factory_image_verify.py image.zip
+
+The list of canonical Pixel factory images can be found here:
+https://developers.google.com/android/images
+
+Supported are all factory images of Pixel 3 and later devices.
+
+In order for the tool to run correct the following utilities need to be
+pre-installed: wget, unzip.
+
+The tool also runs outside of the repository location as long as the working
+directory is writable.
+"""
+
+from __future__ import print_function
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import distutils.spawn
+
+
+class PixelFactoryImageVerifier(object):
+  """Object for the pixel_factory_image_verify command line tool."""
+
+  def __init__(self):
+    self.working_dir = os.getcwd()
+    self.script_path = os.path.realpath(__file__)
+    self.script_dir = os.path.split(self.script_path)[0]
+    self.avbtool_path = os.path.abspath(os.path.join(self.script_path,
+                                                     '../../../avbtool'))
+
+  def run(self, argv):
+    """Command line processor.
+
+    Args:
+       argv: The command line parameter list.
+    """
+    # Checks for command line parameters and show help if non given.
+    if len(argv) != 2:
+      print('No command line parameter give. At least a filename or URL for a '
+            'Pixel 3 factory image needs to be specified.')
+      sys.exit(1)
+
+    # Checks if necessary commands are available.
+    for cmd in ['unzip', 'wget']:
+      if not distutils.spawn.find_executable(cmd):
+        print('Necessary command line tool needs to be installed first: %s'
+              % cmd)
+        sys.exit(1)
+
+    # Downloads factory image if URL is specified; otherwise treat it as file.
+    if argv[1].lower().startswith('https://'):
+      factory_image_zip = self._download_factory_image(argv[1])
+      if not factory_image_zip:
+        sys.exit(1)
+    else:
+      factory_image_zip = os.path.abspath(argv[1])
+
+    # Unpacks the factory image into partition images.
+    partition_image_dir = self._unpack_factory_image(factory_image_zip)
+    if not partition_image_dir:
+      sys.exit(1)
+
+    # Validates the VBMeta of the factory image.
+    verified = self._verify_vbmeta_partitions(partition_image_dir)
+    if not verified:
+      sys.exit(1)
+
+    # Calculates the VBMeta Digest for the factory image.
+    vbmeta_digest = self._calculate_vbmeta_digest(partition_image_dir)
+    if not vbmeta_digest:
+      sys.exit(1)
+
+    print('The VBMeta Digest for factory image is: %s' % vbmeta_digest)
+    sys.exit(0)
+
+  def _download_factory_image(self, url):
+    """Downloads the factory image to the working directory.
+
+    Args:
+      url: The download URL for the factory image.
+
+    Returns:
+      The absolute path to the factory image or None if it failed.
+    """
+    # Creates temporary download folder.
+    download_path = tempfile.mkdtemp(dir=self.working_dir)
+
+    # Downloads the factory image to the temporary folder.
+    download_filename = self._download_file(download_path, url)
+    if not download_filename:
+      return None
+
+    # Moves the downloaded file into the working directory.
+    download_file = os.path.join(download_path, download_filename)
+    target_file = os.path.join(self.working_dir, download_filename)
+    if os.path.exists(target_file):
+      try:
+        os.remove(target_file)
+      except OSError as e:
+        print('File %s already exists and cannot be deleted.' % download_file)
+        return None
+    try:
+      shutil.move(download_file, self.working_dir)
+    except shutil.Error as e:
+      print('File %s cannot be moved to %s: %s' % (download_file,
+                                                   target_file, e))
+      return None
+
+    # Removes temporary download folder.
+    try:
+      shutil.rmtree(download_path)
+    except shutil.Error as e:
+      print('Temporary download folder %s could not be removed.'
+            % download_path)
+    return os.path.join(self.working_dir, download_filename)
+
+  def _download_file(self, download_dir, url):
+    """Downloads a file from the Internet.
+
+    Args:
+      download_dir: The folder the file should be downloaded to.
+      url: The download URL for the file.
+
+    Returns:
+      The name of the downloaded file as it apears on disk; otherwise None
+      if download failed.
+    """
+    print('Fetching file from: %s' % url)
+    os.chdir(download_dir)
+    args = ['wget', url]
+    result, _ = self._run_command(args,
+                                  'Successfully downloaded file.',
+                                  'File download failed.')
+    os.chdir(self.working_dir)
+    if not result:
+      return None
+
+    # Figure out the file name of what was downloaded: It will be the only file
+    # in the download folder.
+    files = os.listdir(download_dir)
+    if files and len(files) == 1:
+      return files[0]
+    else:
+      return None
+
+  def _unpack_factory_image(self, factory_image_file):
+    """Unpacks the factory image zip file.
+
+    Args:
+      factory_image_file: path and file name to the image file.
+
+    Returns:
+      The path to the folder which contains the unpacked factory image files or
+      None if it failed.
+    """
+    unpack_dir = tempfile.mkdtemp(dir=self.working_dir)
+    args = ['unzip', factory_image_file, '-d', unpack_dir]
+    result, _ = self._run_command(args,
+                                  'Successfully unpacked factory image.',
+                                  'Failed to unpack factory image.')
+    if not result:
+      return None
+
+    # Locate the directory which contains the image files.
+    files = os.listdir(unpack_dir)
+    image_name = None
+    for f in files:
+      path = os.path.join(self.working_dir, unpack_dir, f)
+      if os.path.isdir(path):
+        image_name = f
+        break
+    if not image_name:
+      print('No image found: %s' % image_name)
+      return None
+
+    # Move image file directory to the working directory
+    image_dir = os.path.join(unpack_dir, image_name)
+    target_dir = os.path.join(self.working_dir, image_name)
+    if os.path.exists(target_dir):
+      try:
+        shutil.rmtree(target_dir)
+      except shutil.Error as e:
+        print('Directory %s already exists and cannot be deleted.' % target_dir)
+        return None
+
+    try:
+      shutil.move(image_dir, self.working_dir)
+    except shutil.Error as e:
+      print('Directory %s could not be moved to %s: %s' % (image_dir,
+                                                           self.working_dir, e))
+      return None
+
+    # Removes tmp unpack directory.
+    try:
+      shutil.rmtree(unpack_dir)
+    except shutil.Error as e:
+      print('Temporary download folder %s could not be removed.'
+            % unpack_dir)
+
+    # Unzip the secondary zip file which contain the individual images.
+    image_filename = 'image-%s' % image_name
+    image_folder = os.path.join(self.working_dir, image_name)
+    os.chdir(image_folder)
+
+    args = ['unzip', image_filename]
+    result, _ = self._run_command(
+        args,
+        'Successfully unpacked factory image partitions.',
+        'Failed to unpack factory image partitions.')
+    if not result:
+      return None
+    return image_folder
+
+  def _verify_vbmeta_partitions(self, image_dir):
+    """Verifies all partitions protected by VBMeta using avbtool verify_image.
+
+    Args:
+      image_dir: The folder containing the unpacked factory image partitions,
+      which contains a vbmeta.img patition.
+
+    Returns:
+      True if the VBMeta protected parititions verify.
+    """
+    os.chdir(image_dir)
+    args = [self.avbtool_path,
+            'verify_image',
+            '--image', 'vbmeta.img',
+            '--follow_chain_partitions']
+    result, _ = self._run_command(args,
+                                  'Successfully verified VBmeta.',
+                                  'Verification of VBmeta failed.')
+    os.chdir(self.working_dir)
+    return result
+
+  def _calculate_vbmeta_digest(self, image_dir):
+    """Calculates the VBMeta Digest for given parititions using avbtool.
+
+    Args:
+      image_dir: The folder containing the unpacked factory image partitions,
+        which contains a vbmeta.img partition.
+
+    Returns:
+      Hex string with the VBmeta Digest value or None if it failed.
+    """
+    os.chdir(image_dir)
+    args = [self.avbtool_path,
+            'calculate_vbmeta_digest',
+            '--image', 'vbmeta.img']
+    result, output = self._run_command(args,
+                                       'Successfully calculated VBMeta Digest.',
+                                       'Failed to calculate VBmeta Digest.')
+    if result:
+      return output
+    else:
+      return None
+
+  def _run_command(self, args, success_msg, fail_msg):
+    """Runs command line tools."""
+    p = subprocess.Popen(args, stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    pout, _ = p.communicate()
+    if p.wait() == 0:
+      print(success_msg)
+      return True, pout
+    else:
+      print(fail_msg)
+      return False, pout
+
+
+if __name__ == '__main__':
+  tool = PixelFactoryImageVerifier()
+  tool.run(sys.argv)
+