github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/client/log_client.go (about) 1 // Copyright 2017 Google Inc. 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 client verifies responses from the Trillian log. 16 package client 17 18 import ( 19 "bytes" 20 "context" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/google/trillian" 26 "github.com/google/trillian/client/backoff" 27 "github.com/google/trillian/types" 28 "google.golang.org/grpc/codes" 29 "google.golang.org/grpc/status" 30 ) 31 32 // LogClient represents a client for a given Trillian log instance. 33 type LogClient struct { 34 *LogVerifier 35 LogID int64 36 client trillian.TrillianLogClient 37 root types.LogRootV1 38 rootLock sync.Mutex 39 } 40 41 // New returns a new LogClient. 42 func New(logID int64, client trillian.TrillianLogClient, verifier *LogVerifier) *LogClient { 43 return &LogClient{ 44 LogVerifier: verifier, 45 LogID: logID, 46 client: client, 47 } 48 } 49 50 // NewFromTree creates a new LogClient given a tree config. 51 func NewFromTree(client trillian.TrillianLogClient, config *trillian.Tree) (*LogClient, error) { 52 verifier, err := NewLogVerifierFromTree(config) 53 if err != nil { 54 return nil, err 55 } 56 57 return New(config.GetTreeId(), client, verifier), nil 58 } 59 60 // AddSequencedLeafAndWait adds a leaf at a specific index to the log. 61 // Blocks until it has been included in a signed log root. 62 func (c *LogClient) AddSequencedLeafAndWait(ctx context.Context, data []byte, index int64) error { 63 if err := c.AddSequencedLeaf(ctx, data, index); err != nil { 64 return fmt.Errorf("QueueLeaf(): %v", err) 65 } 66 if err := c.WaitForInclusion(ctx, data); err != nil { 67 return fmt.Errorf("WaitForInclusion(): %v", err) 68 } 69 return nil 70 } 71 72 // AddLeaf adds leaf to the append only log. 73 // Blocks until it gets a verifiable response. 74 func (c *LogClient) AddLeaf(ctx context.Context, data []byte) error { 75 if err := c.QueueLeaf(ctx, data); err != nil { 76 return fmt.Errorf("QueueLeaf(): %v", err) 77 } 78 if err := c.WaitForInclusion(ctx, data); err != nil { 79 return fmt.Errorf("WaitForInclusion(): %v", err) 80 } 81 return nil 82 } 83 84 // GetByIndex returns a single leaf at the requested index. 85 func (c *LogClient) GetByIndex(ctx context.Context, index int64) (*trillian.LogLeaf, error) { 86 resp, err := c.client.GetLeavesByIndex(ctx, &trillian.GetLeavesByIndexRequest{ 87 LogId: c.LogID, 88 LeafIndex: []int64{index}, 89 }) 90 if err != nil { 91 return nil, err 92 } 93 if got, want := len(resp.Leaves), 1; got != want { 94 return nil, fmt.Errorf("len(leaves): %v, want %v", got, want) 95 } 96 return resp.Leaves[0], nil 97 } 98 99 // ListByIndex returns the requested leaves by index. 100 func (c *LogClient) ListByIndex(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { 101 resp, err := c.client.GetLeavesByRange(ctx, 102 &trillian.GetLeavesByRangeRequest{ 103 LogId: c.LogID, 104 StartIndex: start, 105 Count: count, 106 }) 107 if err != nil { 108 return nil, err 109 } 110 // Verify that we got back the requested leaves. 111 if len(resp.Leaves) < int(count) { 112 return nil, fmt.Errorf("len(Leaves)=%d, want %d", len(resp.Leaves), count) 113 } 114 for i, l := range resp.Leaves { 115 if want := start + int64(i); l.LeafIndex != want { 116 return nil, fmt.Errorf("Leaves[%d].LeafIndex=%d, want %d", i, l.LeafIndex, want) 117 } 118 } 119 120 return resp.Leaves, nil 121 } 122 123 // WaitForRootUpdate repeatedly fetches the Root until the fetched tree size >= 124 // waitForTreeSize or until ctx times out. 125 func (c *LogClient) WaitForRootUpdate(ctx context.Context, waitForTreeSize uint64) (*types.LogRootV1, error) { 126 b := &backoff.Backoff{ 127 Min: 100 * time.Millisecond, 128 Max: 10 * time.Second, 129 Factor: 2, 130 Jitter: true, 131 } 132 for i := 0; ; i++ { 133 root, err := c.UpdateRoot(ctx) 134 switch x := status.Code(err); x { 135 case codes.OK: 136 if root.TreeSize >= waitForTreeSize { 137 return root, nil 138 } 139 case codes.Unavailable, codes.NotFound, codes.FailedPrecondition: // Retry. 140 default: 141 return nil, err 142 } 143 144 select { 145 case <-ctx.Done(): 146 return nil, status.Errorf(codes.DeadlineExceeded, "%v", ctx.Err()) 147 case <-time.After(b.Duration()): 148 } 149 } 150 } 151 152 // getLatestRoot fetches and verifies the latest root against a trusted root, seen in the past. 153 // Pass nil for trusted if this is the first time querying this log. 154 func (c *LogClient) getLatestRoot(ctx context.Context, trusted *types.LogRootV1) (*types.LogRootV1, error) { 155 resp, err := c.client.GetLatestSignedLogRoot(ctx, 156 &trillian.GetLatestSignedLogRootRequest{LogId: c.LogID}) 157 if err != nil { 158 return nil, err 159 } 160 161 // TODO(gbelvin): Turn on root verification. 162 /* 163 logRoot, err := c.VerifyRoot(&types.LogRootV1{}, resp.GetSignedLogRoot(), nil) 164 if err != nil { 165 return nil, err 166 } 167 */ 168 // TODO(gbelvin): Remove this hack when all implementations store digital signatures. 169 var logRoot types.LogRootV1 170 if err := logRoot.UnmarshalBinary(resp.GetSignedLogRoot().LogRoot); err != nil { 171 return nil, err 172 } 173 174 if trusted.TreeSize > 0 && 175 logRoot.TreeSize == trusted.TreeSize && 176 bytes.Equal(logRoot.RootHash, trusted.RootHash) { 177 // Tree has not been updated. 178 return &logRoot, nil 179 } 180 // Fetch a consistency proof if this isn't the first root we've seen. 181 var consistency *trillian.GetConsistencyProofResponse 182 if trusted.TreeSize > 0 { 183 // Get consistency proof. 184 consistency, err = c.client.GetConsistencyProof(ctx, 185 &trillian.GetConsistencyProofRequest{ 186 LogId: c.LogID, 187 FirstTreeSize: int64(trusted.TreeSize), 188 SecondTreeSize: int64(logRoot.TreeSize), 189 }) 190 if err != nil { 191 return nil, err 192 } 193 } 194 195 // Verify root update if the tree / the latest signed log root isn't empty. 196 if logRoot.TreeSize > 0 { 197 if _, err := c.VerifyRoot(trusted, resp.GetSignedLogRoot(), 198 consistency.GetProof().GetHashes()); err != nil { 199 return nil, err 200 } 201 } 202 return &logRoot, nil 203 } 204 205 // UpdateRoot retrieves the current SignedLogRoot, verifying it against roots this client has 206 // seen in the past, and updating the currently trusted root if the new root verifies. 207 func (c *LogClient) UpdateRoot(ctx context.Context) (*types.LogRootV1, error) { 208 c.rootLock.Lock() 209 defer c.rootLock.Unlock() 210 211 currentlyTrusted := &c.root 212 newTrusted, err := c.getLatestRoot(ctx, currentlyTrusted) 213 if err != nil { 214 return nil, err 215 } 216 if newTrusted.TimestampNanos > currentlyTrusted.TimestampNanos && 217 newTrusted.TreeSize >= currentlyTrusted.TreeSize { 218 c.root = *newTrusted 219 } 220 // Copy the internal trusted root in order to prevent clients from modifying it. 221 ret := c.root 222 return &ret, nil 223 } 224 225 // WaitForInclusion blocks until the requested data has been verified with an inclusion proof. 226 // This assumes that the data has already been submitted. 227 // Best practice is to call this method with a context that will timeout. 228 func (c *LogClient) WaitForInclusion(ctx context.Context, data []byte) error { 229 leaf, err := c.BuildLeaf(data) 230 if err != nil { 231 return err 232 } 233 234 // Fetch the current Root to improve our chances at a valid inclusion proof. 235 // It is illegal to ask for an inclusion proof with TreeSize = 0. 236 root, err := c.WaitForRootUpdate(ctx, 1) 237 if err != nil { 238 return err 239 } 240 for { 241 ok, err := c.getAndVerifyInclusionProof(ctx, leaf.MerkleLeafHash, root) 242 if err != nil && status.Code(err) != codes.NotFound { 243 return err 244 } 245 if ok { 246 return nil 247 } 248 // Wait for TreeSize to update. 249 if root, err = c.WaitForRootUpdate(ctx, root.TreeSize+1); err != nil { 250 return err 251 } 252 // Retry 253 } 254 } 255 256 // VerifyInclusion updates the log root and ensures that the given leaf data has been included in the log. 257 func (c *LogClient) VerifyInclusion(ctx context.Context, data []byte) error { 258 leaf, err := c.BuildLeaf(data) 259 if err != nil { 260 return err 261 } 262 root, err := c.UpdateRoot(ctx) 263 if err != nil { 264 return fmt.Errorf("UpdateRoot(): %v", err) 265 } 266 ok, err := c.getAndVerifyInclusionProof(ctx, leaf.MerkleLeafHash, root) 267 if err != nil { 268 return err 269 } 270 if !ok { 271 return fmt.Errorf("no proof") 272 } 273 return nil 274 } 275 276 // GetAndVerifyInclusionAtIndex updates the log root and ensures that the given leaf data has been included in the log at a particular index. 277 func (c *LogClient) GetAndVerifyInclusionAtIndex(ctx context.Context, data []byte, index int64) error { 278 root, err := c.UpdateRoot(ctx) 279 if err != nil { 280 return fmt.Errorf("UpdateRoot(): %v", err) 281 } 282 resp, err := c.client.GetInclusionProof(ctx, 283 &trillian.GetInclusionProofRequest{ 284 LogId: c.LogID, 285 LeafIndex: index, 286 TreeSize: int64(root.TreeSize), 287 }) 288 if err != nil { 289 return err 290 } 291 return c.VerifyInclusionAtIndex(root, data, index, resp.Proof.Hashes) 292 } 293 294 func (c *LogClient) getAndVerifyInclusionProof(ctx context.Context, leafHash []byte, sth *types.LogRootV1) (bool, error) { 295 resp, err := c.client.GetInclusionProofByHash(ctx, 296 &trillian.GetInclusionProofByHashRequest{ 297 LogId: c.LogID, 298 LeafHash: leafHash, 299 TreeSize: int64(sth.TreeSize), 300 }) 301 if err != nil { 302 return false, err 303 } 304 if len(resp.Proof) < 1 { 305 return false, nil 306 } 307 for _, proof := range resp.Proof { 308 if err := c.VerifyInclusionByHash(sth, leafHash, proof); err != nil { 309 return false, fmt.Errorf("VerifyInclusionByHash(): %v", err) 310 } 311 } 312 return true, nil 313 } 314 315 // AddSequencedLeaf adds a leaf at a particular index. 316 func (c *LogClient) AddSequencedLeaf(ctx context.Context, data []byte, index int64) error { 317 leaf, err := c.BuildLeaf(data) 318 if err != nil { 319 return err 320 } 321 leaf.LeafIndex = index 322 323 _, err = c.client.AddSequencedLeaf(ctx, &trillian.AddSequencedLeafRequest{ 324 LogId: c.LogID, 325 Leaf: leaf, 326 }) 327 return err 328 } 329 330 // QueueLeaf adds a leaf to a Trillian log without blocking. 331 // AlreadyExists is considered a success case by this function. 332 func (c *LogClient) QueueLeaf(ctx context.Context, data []byte) error { 333 leaf, err := c.BuildLeaf(data) 334 if err != nil { 335 return err 336 } 337 338 _, err = c.client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ 339 LogId: c.LogID, 340 Leaf: leaf, 341 }) 342 return err 343 }