osbase/build/mkverity: generate salt from product info

The salt is now generated from the product info file, instead of the
input image file. This reduces build time by around 0.3 s.

Change-Id: Id3263e24604745324a5652658ff79cc79c9df5fa
Reviewed-on: https://review.monogon.dev/c/monogon/+/4431
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/osbase/build/mkverity/def.bzl b/osbase/build/mkverity/def.bzl
index 417c883..1176060 100644
--- a/osbase/build/mkverity/def.bzl
+++ b/osbase/build/mkverity/def.bzl
@@ -17,22 +17,23 @@
     # Run mkverity.
     image = ctx.actions.declare_file(ctx.attr.name + ".img")
     table = ctx.actions.declare_file(ctx.attr.name + ".dmt")
+    inputs = [ctx.file.source]
+    args = ctx.actions.args()
+    args.add("-input", ctx.file.source)
+    args.add("-output", image)
+    if ctx.file.salt:
+        args.add("-salt", ctx.file.salt)
+        inputs.append(ctx.file.salt)
+    args.add("-table", table)
+    args.add("-data_alias", ctx.attr.rootfs_partlabel)
+    args.add("-hash_alias", ctx.attr.rootfs_partlabel)
     ctx.actions.run(
         mnemonic = "GenVerityImage",
         progress_message = "Generating a dm-verity image: {}".format(image.short_path),
-        inputs = [ctx.file.source],
-        outputs = [
-            image,
-            table,
-        ],
+        inputs = inputs,
+        outputs = [image, table],
         executable = ctx.file._mkverity,
-        arguments = [
-            "-input=" + ctx.file.source.path,
-            "-output=" + image.path,
-            "-table=" + table.path,
-            "-data_alias=" + ctx.attr.rootfs_partlabel,
-            "-hash_alias=" + ctx.attr.rootfs_partlabel,
-        ],
+        arguments = [args],
     )
 
     return [
@@ -56,6 +57,16 @@
         "source": attr.label(
             doc = "A source image.",
             allow_single_file = True,
+            mandatory = True,
+        ),
+        "salt": attr.label(
+            doc = """
+                A file which will be hashed to generate the salt.
+                This should be a small file which is different for each
+                released image, but which only changes when the source also
+                changes. The product info file is a good choice for this.
+            """,
+            allow_single_file = True,
         ),
         "rootfs_partlabel": attr.string(
             doc = "GPT partition label of the rootfs to be used with dm-mod.create.",
diff --git a/osbase/build/mkverity/mkverity.go b/osbase/build/mkverity/mkverity.go
index f75845d..55de7dd 100644
--- a/osbase/build/mkverity/mkverity.go
+++ b/osbase/build/mkverity/mkverity.go
@@ -26,7 +26,7 @@
 // image. Then, the same contents are verity-encoded and appended to the
 // output image. The verity superblock is written only if wsb is true. It
 // returns either a dm-verity target table, or an error.
-func createImage(dataImagePath, outputImagePath string, wsb bool) (*verity.MappingTable, error) {
+func createImage(dataImagePath, outputImagePath, saltPath string, wsb bool) (*verity.MappingTable, error) {
 	// Hardcode both the data block size and the hash block size as 4096 bytes.
 	bs := uint32(4096)
 
@@ -66,20 +66,20 @@
 		return nil, err
 	}
 
-	// Hash the input data for use as the salt, then rewind dataImage. The purpose
-	// of the salt is to prevent reuse of collisions across different images. 16
-	// bytes is enough for this. We use a hash of the input instead of generating
-	// random bytes to make the build reproducible.
-	dataHash := sha256.New()
-	_, err = io.Copy(dataHash, dataImage)
+	// Generate the salt by hashing the salt file. The purpose of the salt is to
+	// prevent reuse of collisions across different images. 16 bytes is enough for
+	// this. We use a hash instead of generating random bytes to make the build
+	// reproducible.
+	saltFile, err := os.Open(saltPath)
+	if err != nil {
+		return nil, fmt.Errorf("while opening the salt file: %w", err)
+	}
+	saltHash := sha256.New()
+	_, err = io.Copy(saltHash, saltFile)
 	if err != nil {
 		return nil, err
 	}
-	_, err = dataImage.Seek(0, io.SeekStart)
-	if err != nil {
-		return nil, err
-	}
-	salt := dataHash.Sum(nil)[:16]
+	salt := saltHash.Sum(nil)[:16]
 
 	// Write outputImage contents. Start with initializing a verity encoder,
 	// setting outputImage as its output.
@@ -112,6 +112,7 @@
 var (
 	input           = flag.String("input", "", "input disk image (required)")
 	output          = flag.String("output", "", "output disk image with Verity metadata appended (required)")
+	salt            = flag.String("salt", "", "input file from which the salt is generated")
 	dataDeviceAlias = flag.String("data_alias", "", "data device alias used in the mapping table")
 	hashDeviceAlias = flag.String("hash_alias", "", "hash device alias used in the mapping table")
 	table           = flag.String("table", "", "a file the mapping table will be saved to; disables stdout")
@@ -128,8 +129,13 @@
 		log.Fatalf("-output must be set.")
 	}
 
+	saltPath := *salt
+	if saltPath == "" {
+		saltPath = *input
+	}
+
 	// Build the image.
-	mt, err := createImage(*input, *output, false)
+	mt, err := createImage(*input, *output, saltPath, false)
 	if err != nil {
 		log.Fatal(err)
 	}