Snap for 8505378 from 02557d9deecd7e1ccc9ba175ca05d06a41ea0c3f to mainline-go-adservices-release

Change-Id: I62c0d700b252d63549f4106d1e1cc8c625d8e601
diff --git a/tools/transparency/verify/README.md b/tools/transparency/verify/README.md
index a8da1ee..c69fb05 100644
--- a/tools/transparency/verify/README.md
+++ b/tools/transparency/verify/README.md
@@ -1,6 +1,6 @@
 # Verifier of Binary Transparency for Pixel Factory Images
 
-This repository contains code to read the transparency log for [Binary Transparency for Pixel Factory Images](https://developers.google.com/android/binary_transparency/pixel).
+This repository contains code to read the transparency log for [Binary Transparency for Pixel Factory Images](https://developers.google.com/android/binary_transparency/pixel). See the particular section for this tool [here](https://developers.google.com/android/binary_transparency/pixel#verifying-image-inclusion-inclusion-proof).
 
 ## Files and Directories
 * `cmd/verifier/`
@@ -16,13 +16,13 @@
 ## Usage
 The verifier uses the checkpoint and the log contents (found at the [tile directory](https://developers.google.com/android/binary_transparency/tile)) to check that your image payload is in the transparency log, i.e. that it is published by Google.
 
-To run the verifier after you have built it in the preious section:
+To run the verifier after you have built it in the previous section:
 ```
-$ ./verifier --payload_path=${PAYLOAD_PATH} --image_info_index=${IMAGE_INFO_INDEX}
+$ ./verifier --payload_path=${PAYLOAD_PATH}
 ```
 
-### Inputs
-The verifier takes two inputs: `payload_path` and `image_info_index`.
+### Input
+The verifier takes a `payload_path` as input.
 
 Each Pixel Factory image corresponds to a [payload](https://developers.google.com/android/binary_transparency/pixel#log-content) stored in the transparency log, the format of which is:
 ```
@@ -30,8 +30,6 @@
 ```
 See [here](https://developers.google.com/android/binary_transparency/pixel#construct-the-payload-for-verification) for a few methods detailing how to extract this payload from an image.
 
-Set `image_info_index` to the index of your payload of interest in this list: [image\_info.txt](https://developers.google.com/android/binary_transparency/image_info.txt).
-
 ### Output
 The output of the command is written to stdout:
   * `OK` if the image is included in the log, i.e. that this [claim](https://developers.google.com/android/binary_transparency/pixel#claimant-model) is true,
diff --git a/tools/transparency/verify/cmd/verifier/verifier.go b/tools/transparency/verify/cmd/verifier/verifier.go
index 08fa727..fd02241 100644
--- a/tools/transparency/verify/cmd/verifier/verifier.go
+++ b/tools/transparency/verify/cmd/verifier/verifier.go
@@ -20,17 +20,14 @@
 package main
 
 import (
-	// Using "flag" and "log" and not their "google3/base/go/" counterparts is
-	// intended in order to reduce google3 dependencies. This code will live in
-	// https://android.googlesource.com/platform/external/avb/+/master/tools/transparency/.
-	"crypto/sha256"
+	"bytes"
 	"flag"
-	"fmt"
 	"log"
 	"os"
+	"path/filepath"
 
-	"github.com/google/binary_transparency/verifier/internal/checkpoint"
-	"github.com/google/binary_transparency/verifier/internal/tiles"
+	"android.googlesource.com/platform/external/avb.git/tools/transparency/verify/internal/checkpoint"
+	"android.googlesource.com/platform/external/avb.git/tools/transparency/verify/internal/tiles"
 	"golang.org/x/mod/sumdb/tlog"
 
 	_ "embed"
@@ -48,20 +45,27 @@
 var logPubKey []byte
 
 var (
-	imageInfoIndex = flag.Int64("image_info_index", -1, "Index representing the image of interest within the image_info.txt log file. Must be in the [0, logSize) range.")
-	payloadPath    = flag.String("payload_path", "", "Path to the payload describing the image of interest.")
-	logBaseURL     = flag.String("log_base_url", "https://developers.google.com/android/binary_transparency", "Base url for the verifiable log files.")
+	payloadPath = flag.String("payload_path", "", "Path to the payload describing the image of interest.")
+	logBaseURL  = flag.String("log_base_url", "https://developers.google.com/android/binary_transparency", "Base url for the verifiable log files.")
 )
 
 func main() {
 	flag.Parse()
 
-	if *imageInfoIndex < 0 {
-		log.Fatal("must specify the image_info_index, in the [0, logSize) range, for the image of interest")
-	}
 	if *payloadPath == "" {
 		log.Fatal("must specify the payload_path for the image payload")
 	}
+	b, err := os.ReadFile(*payloadPath)
+	if err != nil {
+		log.Fatalf("unable to open file %q: %v", *payloadPath, err)
+	}
+	// Payload should not contain excessive leading or trailing whitespace.
+	payloadBytes := bytes.TrimSpace(b)
+	payloadBytes = append(payloadBytes, '\n')
+	if string(b) != string(payloadBytes) {
+		log.Printf("Reformatted payload content from %q to %q", b, payloadBytes)
+	}
+
 
 	v, err := checkpoint.NewVerifier(logPubKey, KeyNameForVerifier)
 	if err != nil {
@@ -72,41 +76,34 @@
 		log.Fatalf("error reading checkpoint for log(%s): %v", *logBaseURL, err)
 	}
 
-	logSize := int64(root.Size)
-	if *imageInfoIndex >= logSize {
-		log.Fatalf("leaf_index must be in the [0, logSize) range: logSize=%d", logSize)
+	m, err := tiles.ImageInfosIndex(*logBaseURL)
+	if err != nil {
+		log.Fatalf("failed to load image info map to find log index: %v", err)
 	}
+	imageInfoIndex, ok := m[string(payloadBytes)]
+	if !ok {
+		log.Fatalf("failed to find payload %q in %s", string(payloadBytes), filepath.Join(*logBaseURL, "image_info.txt"))
+	}
+
 	var th tlog.Hash
 	copy(th[:], root.Hash)
 
+	logSize := int64(root.Size)
 	r := tiles.HashReader{URL: *logBaseURL}
-	rp, err := tlog.ProveRecord(logSize, *imageInfoIndex, r)
+	rp, err := tlog.ProveRecord(logSize, imageInfoIndex, r)
 	if err != nil {
 		log.Fatalf("error in tlog.ProveRecord: %v", err)
 	}
 
-	leafHash, err := payloadHash(*payloadPath)
+	leafHash, err := tiles.PayloadHash(payloadBytes)
 	if err != nil {
 		log.Fatalf("error hashing payload: %v", err)
 	}
 
-	if err := tlog.CheckRecord(rp, logSize, th, *imageInfoIndex, leafHash); err != nil {
+	if err := tlog.CheckRecord(rp, logSize, th, imageInfoIndex, leafHash); err != nil {
 		log.Fatalf("FAILURE: inclusion check error in tlog.CheckRecord: %v", err)
 	} else {
 		log.Print("OK. inclusion check success")
 	}
 }
 
-// payloadHash returns the hash for the payload located at path p.
-func payloadHash(p string) (tlog.Hash, error) {
-	var hash tlog.Hash
-	f, err := os.ReadFile(p)
-	if err != nil {
-		return hash, fmt.Errorf("unable to open file %q: %v", p, err)
-	}
-	l := append([]byte{LeafHashPrefix}, f...)
-	h := sha256.Sum256(l)
-	copy(hash[:], h[:])
-
-	return hash, nil
-}
diff --git a/tools/transparency/verify/go.mod b/tools/transparency/verify/go.mod
index 1ac9c9e..9eb49c9 100644
--- a/tools/transparency/verify/go.mod
+++ b/tools/transparency/verify/go.mod
@@ -1,4 +1,4 @@
-module github.com/google/binary_transparency/verifier
+module android.googlesource.com/platform/external/avb.git/tools/transparency/verify
 
 go 1.17
 
diff --git a/tools/transparency/verify/internal/tiles/reader.go b/tools/transparency/verify/internal/tiles/reader.go
index 5ebbb5e..f998f54 100644
--- a/tools/transparency/verify/internal/tiles/reader.go
+++ b/tools/transparency/verify/internal/tiles/reader.go
@@ -2,11 +2,15 @@
 package tiles
 
 import (
+	"crypto/sha256"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
 	"net/url"
 	"path"
+	"strconv"
+	"strings"
 
 	"golang.org/x/mod/sumdb/tlog"
 )
@@ -17,6 +21,13 @@
 	URL string
 }
 
+
+// Domain separation prefix for Merkle tree hashing with second preimage
+// resistance similar to that used in RFC 6962.
+const (
+	leafHashPrefix = 0
+)
+
 // ReadHashes implements tlog.HashReader's ReadHashes.
 // See: https://pkg.go.dev/golang.org/x/mod/sumdb/tlog#HashReader.
 func (h HashReader) ReadHashes(indices []int64) ([]tlog.Hash, error) {
@@ -47,6 +58,42 @@
 	return hashes, nil
 }
 
+// ImageInfosIndex returns a map from payload to its index in the
+// transparency log according to the image_info.txt.
+func ImageInfosIndex(logBaseURL string) (map[string]int64, error) {
+	b, err := readFromURL(logBaseURL, "image_info.txt")
+	if err != nil {
+		return nil, err
+	}
+
+	imageInfos := string(b)
+	return parseImageInfosIndex(imageInfos)
+}
+
+func parseImageInfosIndex(imageInfos string) (map[string]int64, error) {
+	m := make(map[string]int64)
+
+	infosStr := strings.Split(imageInfos, "\n\n")
+	for _, infoStr := range infosStr {
+		pieces := strings.SplitN(infoStr, "\n", 2)
+		if len(pieces) != 2 {
+			return nil, errors.New("missing newline, malformed image_info.txt")
+		}
+
+		idx, err := strconv.ParseInt(pieces[0], 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("failed to convert %q to int64", pieces[0])
+		}
+
+		// Ensure that each log entry does not have extraneous whitespace, but
+		// also terminates with a newline.
+		logEntry := strings.TrimSpace(pieces[1]) + "\n"
+		m[logEntry] = idx
+	}
+
+	return m, nil
+}
+
 func readFromURL(base, suffix string) ([]byte, error) {
 	u, err := url.Parse(base)
 	if err != nil {
@@ -65,3 +112,13 @@
 
 	return io.ReadAll(resp.Body)
 }
+
+// PayloadHash returns the hash of the payload.
+func PayloadHash(p []byte) (tlog.Hash, error) {
+	l := append([]byte{leafHashPrefix}, p...)
+	h := sha256.Sum256(l)
+
+	var hash tlog.Hash
+	copy(hash[:], h[:])
+	return hash, nil
+}
diff --git a/tools/transparency/verify/internal/tiles/reader_test.go b/tools/transparency/verify/internal/tiles/reader_test.go
index 3d0be09..47e26c3 100644
--- a/tools/transparency/verify/internal/tiles/reader_test.go
+++ b/tools/transparency/verify/internal/tiles/reader_test.go
@@ -141,3 +141,42 @@
 		t.Errorf("wrong ReadHashes result: got %X, want %X", got[0], wantHash)
 	}
 }
+
+func TestParseImageInfosIndex(t *testing.T) {
+	for _, tc := range []struct {
+		desc       string
+		imageInfos string
+		want       map[string]int64
+		wantErr    bool
+	}{
+		{
+			desc:       "size 2",
+			imageInfos: "0\nbuild_fingerprint0\nimage_digest0\n\n1\nbuild_fingerprint1\nimage_digest1\n",
+			wantErr:    false,
+			want: map[string]int64{
+				"build_fingerprint0\nimage_digest0\n": 0,
+				"build_fingerprint1\nimage_digest1\n": 1,
+			},
+		},
+		{
+			desc:       "invalid log entry (no newlines)",
+			imageInfos: "0build_fingerprintimage_digest",
+			wantErr:    true,
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			got, err := parseImageInfosIndex(tc.imageInfos)
+			if err != nil && !tc.wantErr {
+				t.Fatalf("parseImageInfosIndex(%s) received unexpected err %q", tc.imageInfos, err)
+			}
+
+			if err == nil && tc.wantErr {
+				t.Fatalf("parseImageInfosIndex(%s) did not return err, expected err", tc.imageInfos)
+			}
+
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("parseImageInfosIndex returned unexpected diff (-want +got):\n%s", diff)
+			}
+		})
+	}
+}