• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Binary `verifier` checks the inclusion of a particular Pixel Factory Image,
2// identified by its build_fingerprint and vbmeta_digest (the payload), in the
3// Transparency Log.
4//
5// Inputs to the tool are:
6//   - the log leaf index of the image of interest, from the Pixel Binary
7//     Transparency Log, see:
8//     https://developers.google.com/android/binary_transparency/image_info.txt
9//   - the path to a file containing the payload, see this page for instructions
10//     https://developers.google.com/android/binary_transparency/pixel#construct-the-payload-for-verification.
11//   - the log's base URL, if different from the default provided.
12//
13// Outputs:
14//   - "OK" if the image is included in the log,
15//   - "FAILURE" if it isn't.
16//
17// Usage: See README.md.
18// For more details on inclusion proofs, see:
19// https://developers.google.com/android/binary_transparency/pixel#verifying-image-inclusion-inclusion-proof
20package main
21
22import (
23	"bytes"
24	"flag"
25	"log"
26	"os"
27	"path/filepath"
28
29	"android.googlesource.com/platform/external/avb.git/tools/transparency/verify/internal/checkpoint"
30	"android.googlesource.com/platform/external/avb.git/tools/transparency/verify/internal/tiles"
31	"golang.org/x/mod/sumdb/tlog"
32
33	_ "embed"
34)
35
36// Domain separation prefix for Merkle tree hashing with second preimage
37// resistance similar to that used in RFC 6962.
38const (
39	LeafHashPrefix     = 0
40	KeyNameForVerifier = "pixel6_transparency_log"
41)
42
43// See https://developers.google.com/android/binary_transparency/pixel#signature-verification.
44//go:embed log_pub_key.pem
45var logPubKey []byte
46
47var (
48	payloadPath = flag.String("payload_path", "", "Path to the payload describing the image of interest.")
49	logBaseURL  = flag.String("log_base_url", "https://developers.google.com/android/binary_transparency", "Base url for the verifiable log files.")
50)
51
52func main() {
53	flag.Parse()
54
55	if *payloadPath == "" {
56		log.Fatal("must specify the payload_path for the image payload")
57	}
58	b, err := os.ReadFile(*payloadPath)
59	if err != nil {
60		log.Fatalf("unable to open file %q: %v", *payloadPath, err)
61	}
62	// Payload should not contain excessive leading or trailing whitespace.
63	payloadBytes := bytes.TrimSpace(b)
64	payloadBytes = append(payloadBytes, '\n')
65	if string(b) != string(payloadBytes) {
66		log.Printf("Reformatted payload content from %q to %q", b, payloadBytes)
67	}
68
69
70	v, err := checkpoint.NewVerifier(logPubKey, KeyNameForVerifier)
71	if err != nil {
72		log.Fatalf("error creating verifier: %v", err)
73	}
74	root, err := checkpoint.FromURL(*logBaseURL, v)
75	if err != nil {
76		log.Fatalf("error reading checkpoint for log(%s): %v", *logBaseURL, err)
77	}
78
79	m, err := tiles.ImageInfosIndex(*logBaseURL)
80	if err != nil {
81		log.Fatalf("failed to load image info map to find log index: %v", err)
82	}
83	imageInfoIndex, ok := m[string(payloadBytes)]
84	if !ok {
85		log.Fatalf("failed to find payload %q in %s", string(payloadBytes), filepath.Join(*logBaseURL, "image_info.txt"))
86	}
87
88	var th tlog.Hash
89	copy(th[:], root.Hash)
90
91	logSize := int64(root.Size)
92	r := tiles.HashReader{URL: *logBaseURL}
93	rp, err := tlog.ProveRecord(logSize, imageInfoIndex, r)
94	if err != nil {
95		log.Fatalf("error in tlog.ProveRecord: %v", err)
96	}
97
98	leafHash, err := tiles.PayloadHash(payloadBytes)
99	if err != nil {
100		log.Fatalf("error hashing payload: %v", err)
101	}
102
103	if err := tlog.CheckRecord(rp, logSize, th, imageInfoIndex, leafHash); err != nil {
104		log.Fatalf("FAILURE: inclusion check error in tlog.CheckRecord: %v", err)
105	} else {
106		log.Print("OK. inclusion check success")
107	}
108}
109
110