avbtool: add support for signing helper.

Right now avbtool simply uses 'openssl' program for signing hashes. A
key is passed via the --key options. It is fine for development and
testing but it is a problem if we are trying to integrate with an existing
singing server.

Intoduce the --signing_helper option which can be used to specify any
external program for signing hashes. The data to sign is fed via STDIN
and the sighed data is returned via STDOUT. If --signing_helper is
specified in a command line, the --key option need only contain a public key.

Bug: 33586278
Test: New unit tests and all unit tests pass.
Change-Id: I74d4b5f076f8a25a29924394fff4708bc1ebf970
Signed-off-by: Dzmitry Yatsushkevich <dmitryya@google.com>
diff --git a/avbtool b/avbtool
index 3a04de6..aaa7f50 100755
--- a/avbtool
+++ b/avbtool
@@ -1813,7 +1813,7 @@
                         key_path, public_key_metadata_path, rollback_index,
                         flags, props, props_from_file, kernel_cmdlines,
                         generate_dm_verity_cmdline_from_hashtree,
-                        include_descriptors_from_image):
+                        include_descriptors_from_image, signing_helper):
     """Implements the 'make_vbmeta_image' command.
 
     Arguments:
@@ -1829,6 +1829,7 @@
       kernel_cmdlines: Kernel cmdlines to insert (list of strings).
       generate_dm_verity_cmdline_from_hashtree: 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.
 
     Raises:
       AvbError: If a chained partition is malformed.
@@ -1855,7 +1856,7 @@
         algorithm_name, key_path, public_key_metadata_path, descriptors,
         rollback_index, flags, props, props_from_file, kernel_cmdlines,
         generate_dm_verity_cmdline_from_hashtree,
-        include_descriptors_from_image)
+        include_descriptors_from_image, signing_helper)
 
     # Write entire vbmeta blob (header, authentication, auxiliary).
     output.seek(0)
@@ -1866,7 +1867,7 @@
                             rollback_index, flags, props, props_from_file,
                             kernel_cmdlines,
                             generate_dm_verity_cmdline_from_hashtree,
-                            include_descriptors_from_image):
+                            include_descriptors_from_image, signing_helper):
     """Generates a VBMeta blob.
 
     This blob contains the header (struct AvbVBMetaHeader), the
@@ -1891,6 +1892,7 @@
         dm-verity kernel cmdline from.
       include_descriptors_from_image: List of file objects for which
         to insert descriptors from.
+      signing_helper: Program which signs a hash and return signature.
 
     Returns:
       A bytearray() with the VBMeta blob.
@@ -2029,11 +2031,19 @@
       binary_hash.extend(ha.digest())
 
       # Calculate the signature.
-      p = subprocess.Popen(
-          ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
-          stdin=subprocess.PIPE,
-          stdout=subprocess.PIPE,
-          stderr=subprocess.PIPE)
+      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)
+      else:
+          p = subprocess.Popen(
+              ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
+              stdin=subprocess.PIPE,
+              stdout=subprocess.PIPE,
+              stderr=subprocess.PIPE)
       padding_and_hash = str(bytearray(alg.padding)) + binary_hash
       (pout, perr) = p.communicate(padding_and_hash)
       retcode = p.wait()
@@ -2065,7 +2075,7 @@
                       public_key_metadata_path, rollback_index, props,
                       props_from_file, kernel_cmdlines,
                       generate_dm_verity_cmdline_from_hashtree,
-                      include_descriptors_from_image):
+                      include_descriptors_from_image, signing_helper):
     """Implementation of the add_hash_footer on unsparse images.
 
     Arguments:
@@ -2085,6 +2095,7 @@
         dm-verity kernel cmdline from.
       include_descriptors_from_image: List of file objects for which
         to insert descriptors from.
+      signing_helper: Program which signs a hash and return signature.
 
     Raises:
       AvbError: If an argument is incorrect.
@@ -2159,7 +2170,7 @@
           algorithm_name, key_path, public_key_metadata_path, [h_desc],
           rollback_index, flags, props, props_from_file, kernel_cmdlines,
           generate_dm_verity_cmdline_from_hashtree,
-          include_descriptors_from_image)
+          include_descriptors_from_image, signing_helper)
 
       # 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.
@@ -2208,7 +2219,7 @@
                           props, props_from_file, kernel_cmdlines,
                           generate_dm_verity_cmdline_from_hashtree,
                           include_descriptors_from_image,
-                          calc_max_image_size):
+                          calc_max_image_size, signing_helper):
     """Implements the 'add_hashtree_footer' command.
 
     See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
@@ -2237,6 +2248,7 @@
       calc_max_image_size: Don't store the hashtree or footer - instead
         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.
 
     Raises:
       AvbError: If an argument is incorrect.
@@ -2373,7 +2385,7 @@
           algorithm_name, key_path, public_key_metadata_path, [ht_desc],
           rollback_index, flags, props, props_from_file, kernel_cmdlines,
           generate_dm_verity_cmdline_from_hashtree,
-          include_descriptors_from_image)
+          include_descriptors_from_image, signing_helper)
       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
                         len(vbmeta_blob))
       vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
@@ -2580,6 +2592,11 @@
                             help='Path to RSA private key file',
                             metavar='KEY',
                             required=False)
+    sub_parser.add_argument('--signing_helper',
+                            help='Path to helper used for signing',
+                            metavar='APP',
+                            default=None,
+                            required=False)
     sub_parser.add_argument('--public_key_metadata',
                             help='Path to public key metadata file',
                             metavar='KEY_METADATA',
@@ -2772,7 +2789,7 @@
                                args.flags, args.prop, args.prop_from_file,
                                args.kernel_cmdline,
                                args.generate_dm_verity_cmdline_from_hashtree,
-                               args.include_descriptors_from_image)
+                               args.include_descriptors_from_image, args.signing_helper)
 
   def add_hash_footer(self, args):
     """Implements the 'add_hash_footer' sub-command."""
@@ -2783,7 +2800,7 @@
                              args.prop, args.prop_from_file,
                              args.kernel_cmdline,
                              args.generate_dm_verity_cmdline_from_hashtree,
-                             args.include_descriptors_from_image)
+                             args.include_descriptors_from_image, args.signing_helper)
 
   def add_hashtree_footer(self, args):
     """Implements the 'add_hashtree_footer' sub-command."""
@@ -2799,7 +2816,7 @@
                                  args.kernel_cmdline,
                                  args.generate_dm_verity_cmdline_from_hashtree,
                                  args.include_descriptors_from_image,
-                                 args.calc_max_image_size)
+                                 args.calc_max_image_size, args.signing_helper)
 
   def erase_footer(self, args):
     """Implements the 'erase_footer' sub-command."""