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