xref: /aosp_15_r20/external/avb/tools/transparency/verify/cmd/verifier/verifier.go (revision d289c2ba6de359471b23d594623b906876bc48a0)
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_verification#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_verification#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	KeyNameForVerifierPixel = "pixel_transparency_log"
41	KeyNameForVerifierG1P   = "developers.google.com/android/binary_transparency/google1p/0"
42	LogBaseURLPixel         = "https://developers.google.com/android/binary_transparency"
43	LogBaseURLG1P           = "https://developers.google.com/android/binary_transparency/google1p"
44	ImageInfoFilename       = "image_info.txt"
45	PackageInfoFilename     = "package_info.txt"
46)
47
48// See https://developers.google.com/android/binary_transparency/pixel_tech_details#log_implementation.
49//
50//go:embed log_pub_key.pixel.pem
51var pixelLogPubKey []byte
52
53// See https://developers.google.com/android/binary_transparency/google1p/log_details#log_implementation.
54//
55//go:embed log_pub_key.google_system_apk.pem
56var googleSystemAppLogPubKey []byte
57
58var (
59	payloadPath = flag.String("payload_path", "", "Path to the payload describing the binary of interest.")
60	logType     = flag.String("log_type", "", "Which log: 'pixel' or 'google_system_apk'.")
61)
62
63func main() {
64	flag.Parse()
65
66	if *payloadPath == "" {
67		log.Fatal("must specify the payload_path for the image payload")
68	}
69	b, err := os.ReadFile(*payloadPath)
70	if err != nil {
71		log.Fatalf("unable to open file %q: %v", *payloadPath, err)
72	}
73	// Payload should not contain excessive leading or trailing whitespace.
74	payloadBytes := bytes.TrimSpace(b)
75	payloadBytes = append(payloadBytes, '\n')
76	if string(b) != string(payloadBytes) {
77		log.Printf("Reformatted payload content from %q to %q", b, payloadBytes)
78	}
79
80	var logPubKey []byte
81	var logBaseURL string
82	var keyNameForVerifier string
83	var binaryInfoFilename string
84	if *logType == "" {
85		log.Fatal("must specify which log to verify against: 'pixel' or 'google_system_apk'")
86	} else if *logType == "pixel" {
87		logPubKey = pixelLogPubKey
88		logBaseURL = LogBaseURLPixel
89		keyNameForVerifier = KeyNameForVerifierPixel
90		binaryInfoFilename = ImageInfoFilename
91	} else if *logType == "google_system_apk" {
92		logPubKey = googleSystemAppLogPubKey
93		logBaseURL = LogBaseURLG1P
94		keyNameForVerifier = KeyNameForVerifierG1P
95		binaryInfoFilename = PackageInfoFilename
96	} else {
97		log.Fatal("unsupported log type")
98	}
99
100	v, err := checkpoint.NewVerifier(logPubKey, keyNameForVerifier)
101	if err != nil {
102		log.Fatalf("error creating verifier: %v", err)
103	}
104	root, err := checkpoint.FromURL(logBaseURL, v)
105	if err != nil {
106		log.Fatalf("error reading checkpoint for log(%s): %v", logBaseURL, err)
107	}
108
109	m, err := tiles.BinaryInfosIndex(logBaseURL, binaryInfoFilename)
110	if err != nil {
111		log.Fatalf("failed to load binary info map to find log index: %v", err)
112	}
113	binaryInfoIndex, ok := m[string(payloadBytes)]
114	if !ok {
115		log.Fatalf("failed to find payload %q in %s", string(payloadBytes), filepath.Join(logBaseURL, binaryInfoFilename))
116	}
117
118	var th tlog.Hash
119	copy(th[:], root.Hash)
120
121	logSize := int64(root.Size)
122	r := tiles.HashReader{URL: logBaseURL}
123	rp, err := tlog.ProveRecord(logSize, binaryInfoIndex, r)
124	if err != nil {
125		log.Fatalf("error in tlog.ProveRecord: %v", err)
126	}
127
128	leafHash, err := tiles.PayloadHash(payloadBytes)
129	if err != nil {
130		log.Fatalf("error hashing payload: %v", err)
131	}
132
133	if err := tlog.CheckRecord(rp, logSize, th, binaryInfoIndex, leafHash); err != nil {
134		log.Fatalf("FAILURE: inclusion check error in tlog.CheckRecord: %v", err)
135	} else {
136		log.Print("OK. inclusion check success!")
137	}
138}
139