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