github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/binary_transparency/firmware/cmd/ft_witness/internal/http/witness.go (about) 1 // Copyright 2021 Google LLC. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package http contains private implementation details for the FirmwareTransparency witness. 16 package http 17 18 import ( 19 "context" 20 "fmt" 21 "net/http" 22 "net/url" 23 "sync" 24 "time" 25 26 "github.com/golang/glog" 27 "github.com/google/trillian-examples/binary_transparency/firmware/api" 28 "github.com/google/trillian-examples/binary_transparency/firmware/internal/client" 29 "github.com/gorilla/mux" 30 "golang.org/x/mod/sumdb/note" 31 ) 32 33 // WitnessStore is the interface to the Witness Store, for storage of latest checkpoint 34 type WitnessStore interface { 35 // Store puts the checkpoint into Witness Store 36 StoreCP([]byte) error 37 38 // Retrieve gets the stored checkpoint. 39 RetrieveCP() ([]byte, error) 40 } 41 42 // Witness is the core state & handler implementation of the FT Witness 43 type Witness struct { 44 ws WitnessStore 45 gcp api.LogCheckpoint 46 logURL string 47 logSigVerifier note.Verifier 48 pollInterval time.Duration 49 witnessLock sync.Mutex 50 } 51 52 // NewWitness creates a new Witness. 53 func NewWitness(ws WitnessStore, logURL string, logSigVerifier note.Verifier, pollInterval time.Duration) (*Witness, error) { 54 gcpRaw, err := ws.RetrieveCP() 55 if err != nil { 56 return nil, fmt.Errorf("new witness failed due to storage retrieval: %w", err) 57 } 58 gcp := api.LogCheckpoint{ 59 Envelope: gcpRaw, 60 } 61 if len(gcpRaw) > 0 { 62 cp, err := api.ParseCheckpoint(gcpRaw, logSigVerifier) 63 if err != nil { 64 return nil, fmt.Errorf("failed to open stored checkpoint: %w", err) 65 } 66 gcp = *cp 67 } 68 69 return &Witness{ 70 ws: ws, 71 gcp: gcp, 72 logURL: logURL, 73 logSigVerifier: logSigVerifier, 74 pollInterval: pollInterval, 75 }, nil 76 } 77 78 // getCheckpoint returns a checkpoint which is registered with witness 79 func (s *Witness) getCheckpoint(w http.ResponseWriter, r *http.Request) { 80 s.witnessLock.Lock() 81 w.Header().Set("Content-Type", "text/plain") 82 if _, err := w.Write(s.gcp.Envelope); err != nil { 83 glog.Errorf("w.Write(): %v", err) 84 } 85 } 86 87 // RegisterHandlers registers HTTP handlers for firmware transparency endpoints. 88 func (s *Witness) RegisterHandlers(r *mux.Router) { 89 r.HandleFunc(fmt.Sprintf("/%s", api.WitnessGetCheckpoint), s.getCheckpoint).Methods("GET") 90 } 91 92 // Poll periodically polls the FT log for updating the witness checkpoint. 93 // It only returns on error (when it doesn't start its own polling thread) 94 func (s *Witness) Poll(ctx context.Context) error { 95 ftURL, err := url.Parse(s.logURL) 96 if err != nil { 97 return fmt.Errorf("failed to parse FT log URL: %w", err) 98 } 99 c := client.ReadonlyClient{ 100 LogURL: ftURL, 101 LogSigVerifier: s.logSigVerifier, 102 } 103 follow := client.NewLogFollower(c) 104 105 glog.Infof("Polling FT log %q...", ftURL) 106 cpc, cperrc := follow.Checkpoints(ctx, s.pollInterval, s.gcp) 107 108 for { 109 var cp api.LogCheckpoint 110 select { 111 case err = <-cperrc: 112 return err 113 case <-ctx.Done(): 114 return ctx.Err() 115 case cp = <-cpc: 116 } 117 118 s.witnessLock.Lock() 119 if err = s.ws.StoreCP(cp.Envelope); err != nil { 120 glog.Warningf("Failed to save new logcheckpoint into store: %q", err) 121 s.witnessLock.Unlock() 122 continue 123 } 124 s.gcp = cp 125 s.witnessLock.Unlock() 126 } 127 }