diff options
author | Jay Hou <jayhou@google.com> | 2022-03-24 02:46:39 -0700 |
---|---|---|
committer | Jay Hou <jayhou@google.com> | 2022-03-27 14:20:50 -0700 |
commit | 4492f6f2d9033c682b2792fe4eb2da5af0a20ecb (patch) | |
tree | 6f7a54c515f3d31d2bc912f56d1415096c7588b2 | |
parent | 8ffd7d65992dd402d3832d0c2f6c948433543c4e (diff) | |
download | avb-4492f6f2d9033c682b2792fe4eb2da5af0a20ecb.tar.gz |
Look up payload (log entry) from image_info.txt rather than ask for the index as a parameter.
This provides for a better user experience, since we have this information on hand already.
Test: go test ./... and ran ./verifier with payload.
Signed-off-by: Jay Hou <jayhou@google.com>
Change-Id: Ia6d06dba7855352cecdcbaa72ad6ec5d898f7c13
-rw-r--r-- | tools/transparency/verify/README.md | 12 | ||||
-rw-r--r-- | tools/transparency/verify/cmd/verifier/verifier.go | 57 | ||||
-rw-r--r-- | tools/transparency/verify/internal/tiles/reader.go | 39 | ||||
-rw-r--r-- | tools/transparency/verify/internal/tiles/reader_test.go | 39 |
4 files changed, 115 insertions, 32 deletions
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 @@ An executable named `verifier` should be produced upon successful build. ## 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 @@ Each Pixel Factory image corresponds to a [payload](https://developers.google.co ``` 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..d565f63 100644 --- a/tools/transparency/verify/cmd/verifier/verifier.go +++ b/tools/transparency/verify/cmd/verifier/verifier.go @@ -20,14 +20,12 @@ 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/. + "bytes" "crypto/sha256" "flag" - "fmt" "log" "os" + "path/filepath" "github.com/google/binary_transparency/verifier/internal/checkpoint" "github.com/google/binary_transparency/verifier/internal/tiles" @@ -48,20 +46,27 @@ const ( 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 +77,43 @@ func main() { 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 := 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...) +// payloadHash returns the hash of the payload. +func payloadHash(p []byte) (tlog.Hash, error) { + l := append([]byte{LeafHashPrefix}, p...) h := sha256.Sum256(l) - copy(hash[:], h[:]) + var hash tlog.Hash + copy(hash[:], h[:]) return hash, nil } diff --git a/tools/transparency/verify/internal/tiles/reader.go b/tools/transparency/verify/internal/tiles/reader.go index 5ebbb5e..b0993f1 100644 --- a/tools/transparency/verify/internal/tiles/reader.go +++ b/tools/transparency/verify/internal/tiles/reader.go @@ -2,11 +2,14 @@ package tiles import ( + "errors" "fmt" "io" "net/http" "net/url" "path" + "strconv" + "strings" "golang.org/x/mod/sumdb/tlog" ) @@ -47,6 +50,42 @@ func (h HashReader) ReadHashes(indices []int64) ([]tlog.Hash, error) { 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 { 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 @@ func TestReadHashesCachedTile(t *testing.T) { 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) + } + }) + } +} |