github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/merkle/merkle_path.go (about) 1 // Copyright 2016 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 merkle 16 17 import ( 18 "errors" 19 "fmt" 20 "math/bits" 21 22 "github.com/golang/glog" 23 "github.com/google/trillian/storage" 24 "google.golang.org/grpc/codes" 25 "google.golang.org/grpc/status" 26 ) 27 28 // Verbosity levels for logging of debug related items 29 const vLevel = 2 30 const vvLevel = 4 31 32 // NodeFetch bundles a nodeID with additional information on how to use the node to construct the 33 // correct proof. 34 type NodeFetch struct { 35 NodeID storage.NodeID 36 Rehash bool 37 } 38 39 // Equivalent return true iff the other represents the same rehash state and NodeID as the other. 40 func (n NodeFetch) Equivalent(other NodeFetch) bool { 41 return n.Rehash == other.Rehash && n.NodeID.Equivalent(other.NodeID) 42 } 43 44 // checkSnapshot performs a couple of simple sanity checks on ss and treeSize 45 // and returns an error if there's a problem. 46 func checkSnapshot(ssDesc string, ss, treeSize int64) error { 47 if ss < 1 { 48 return fmt.Errorf("%s %d < 1", ssDesc, ss) 49 } 50 if ss > treeSize { 51 return fmt.Errorf("%s %d > treeSize %d", ssDesc, ss, treeSize) 52 } 53 return nil 54 } 55 56 // CalcInclusionProofNodeAddresses returns the tree node IDs needed to 57 // build an inclusion proof for a specified leaf and tree size. The snapshot parameter 58 // is the tree size being queried for, treeSize is the actual size of the tree at the revision 59 // we are using to fetch nodes (this can be > snapshot). The maxBitLen parameter 60 // is copied into all the returned nodeIDs. 61 func CalcInclusionProofNodeAddresses(snapshot, index, treeSize int64, maxBitLen int) ([]NodeFetch, error) { 62 if err := checkSnapshot("snapshot", snapshot, treeSize); err != nil { 63 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for inclusion proof: %v", err) 64 } 65 if index >= snapshot { 66 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for inclusion proof: index %d is >= snapshot %d", index, snapshot) 67 } 68 if index < 0 { 69 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for inclusion proof: index %d is < 0", index) 70 } 71 if maxBitLen <= 0 { 72 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for inclusion proof: maxBitLen %d <= 0", maxBitLen) 73 } 74 75 return pathFromNodeToRootAtSnapshot(index, 0, snapshot, treeSize, maxBitLen) 76 } 77 78 // CalcConsistencyProofNodeAddresses returns the tree node IDs needed to 79 // build a consistency proof between two specified tree sizes. snapshot1 and snapshot2 represent 80 // the two tree sizes for which consistency should be proved, treeSize is the actual size of the 81 // tree at the revision we are using to fetch nodes (this can be > snapshot2). The maxBitLen 82 // parameter is copied into all the returned nodeIDs. The caller is responsible for checking that 83 // the input tree sizes correspond to valid tree heads. All returned NodeIDs are tree 84 // coordinates within the new tree. It is assumed that they will be fetched from storage 85 // at a revision corresponding to the STH associated with the treeSize parameter. 86 func CalcConsistencyProofNodeAddresses(snapshot1, snapshot2, treeSize int64, maxBitLen int) ([]NodeFetch, error) { 87 if err := checkSnapshot("snapshot1", snapshot1, treeSize); err != nil { 88 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for consistency proof: %v", err) 89 } 90 if err := checkSnapshot("snapshot2", snapshot2, treeSize); err != nil { 91 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for consistency proof: %v", err) 92 } 93 if snapshot1 > snapshot2 { 94 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for consistency proof: snapshot1 %d > snapshot2 %d", snapshot1, snapshot2) 95 } 96 if maxBitLen <= 0 { 97 return nil, status.Errorf(codes.InvalidArgument, "invalid parameter for consistency proof: maxBitLen %d <= 0", maxBitLen) 98 } 99 100 return snapshotConsistency(snapshot1, snapshot2, treeSize, maxBitLen) 101 } 102 103 // snapshotConsistency does the calculation of consistency proof node addresses between 104 // two snapshots. Based on the C++ code used by CT but adjusted to fit our situation. 105 func snapshotConsistency(snapshot1, snapshot2, treeSize int64, maxBitLen int) ([]NodeFetch, error) { 106 proof := make([]NodeFetch, 0, bits.Len64(uint64(snapshot2))+1) 107 108 glog.V(vLevel).Infof("snapshotConsistency: %d -> %d", snapshot1, snapshot2) 109 110 if snapshot1 == snapshot2 { 111 return proof, nil 112 } 113 114 level := 0 115 node := snapshot1 - 1 116 117 // Compute the (compressed) path to the root of snapshot2. 118 // Everything left of 'node' is equal in both trees; no need to record. 119 for (node & 1) != 0 { 120 glog.V(vvLevel).Infof("Move up: l:%d n:%d", level, node) 121 node >>= 1 122 level++ 123 } 124 125 if node != 0 { 126 glog.V(vvLevel).Infof("Not root snapshot1: %d", node) 127 // Not at the root of snapshot 1, record the node 128 n, err := storage.NewNodeIDForTreeCoords(int64(level), node, maxBitLen) 129 if err != nil { 130 return nil, err 131 } 132 proof = append(proof, NodeFetch{NodeID: n}) 133 } 134 135 // Now append the path from this node to the root of snapshot2. 136 p, err := pathFromNodeToRootAtSnapshot(node, level, snapshot2, treeSize, maxBitLen) 137 if err != nil { 138 return nil, err 139 } 140 return append(proof, p...), nil 141 } 142 143 func pathFromNodeToRootAtSnapshot(node int64, level int, snapshot, treeSize int64, maxBitLen int) ([]NodeFetch, error) { 144 glog.V(vLevel).Infof("pathFromNodeToRootAtSnapshot(%d, %d, %d, %d, %d)", node, level, snapshot, treeSize, maxBitLen) 145 proof := make([]NodeFetch, 0, bits.Len64(uint64(snapshot))+1) 146 147 if snapshot == 0 { 148 return proof, nil 149 } 150 151 // Index of the last node (if the level is fully populated). 152 lastNode := (snapshot - 1) >> uint(level) 153 154 // Move up, recording the sibling of the current node at each level. 155 for lastNode != 0 { 156 sibling := node ^ 1 157 if sibling < lastNode { 158 // The sibling is not the last node of the level in the snapshot tree 159 glog.V(vvLevel).Infof("Not last: S:%d L:%d", sibling, level) 160 n, err := storage.NewNodeIDForTreeCoords(int64(level), sibling, maxBitLen) 161 if err != nil { 162 return nil, err 163 } 164 proof = append(proof, NodeFetch{NodeID: n}) 165 } else if sibling == lastNode { 166 // The sibling is the last node of the level in the snapshot tree. 167 // We might need to recompute a previous hash value here. This can only occur on the 168 // rightmost tree nodes because this is the only area of the tree that is not fully populated. 169 glog.V(vvLevel).Infof("Last: S:%d L:%d", sibling, level) 170 171 if snapshot == treeSize { 172 // No recomputation required as we're using the tree in its current state 173 // Account for non existent nodes - these can only be the rightmost node at an 174 // intermediate (non leaf) level in the tree so will always be a right sibling. 175 n, err := siblingIDSkipLevels(snapshot, lastNode, level, node, maxBitLen) 176 if err != nil { 177 return nil, err 178 } 179 proof = append(proof, NodeFetch{NodeID: n}) 180 } else { 181 // We need to recompute this node, as it was at the prior snapshot point. We record 182 // the additional fetches needed to do this later 183 rehashFetches, err := recomputePastSnapshot(snapshot, treeSize, level, maxBitLen) 184 if err != nil { 185 return nil, err 186 } 187 188 // Extra check that the recomputation produced one node 189 if err = checkRecomputation(rehashFetches); err != nil { 190 return nil, err 191 } 192 193 proof = append(proof, rehashFetches...) 194 } 195 } else { 196 glog.V(vvLevel).Infof("Nonexistent: S:%d L:%d", sibling, level) 197 } 198 199 // Sibling > lastNode so does not exist, move up 200 node >>= 1 201 lastNode >>= 1 202 level++ 203 } 204 205 return proof, nil 206 } 207 208 // recomputePastSnapshot does the work to recalculate nodes that need to be rehashed because the 209 // tree state at the snapshot size differs from the size we've stored it at. The calculations 210 // also need to take into account missing levels, see the tree diagrams in this file. 211 // If called with snapshot equal to the tree size returns empty. Otherwise, assuming no errors, 212 // the output of this should always be exactly one node after resolving any rehashing. 213 // Either a copy of one of the nodes in the tree or a rehashing of multiple nodes to a single 214 // result node with the value it would have had if the prior snapshot had been stored. 215 func recomputePastSnapshot(snapshot, treeSize int64, nodeLevel int, maxBitlen int) ([]NodeFetch, error) { 216 glog.V(vLevel).Infof("recompute s:%d ts:%d level:%d", snapshot, treeSize, nodeLevel) 217 218 fetches := []NodeFetch{} 219 220 if snapshot == treeSize { 221 // Nothing to do 222 return nil, nil 223 } else if snapshot > treeSize { 224 return nil, fmt.Errorf("recomputePastSnapshot: %d does not exist for tree of size %d", snapshot, treeSize) 225 } 226 227 // We're recomputing the right hand path, the one to the last leaf 228 level := 0 229 // This is the index of the last node in the snapshot 230 lastNode := snapshot - 1 231 // This is the index of the last node that actually exists in the underlying tree 232 lastNodeAtLevel := treeSize - 1 233 234 // Work up towards the root. We may find the node we need without needing to rehash if 235 // it turns out that the tree is complete up to the level we're recalculating at this 236 // snapshot. 237 for (lastNode & 1) != 0 { 238 if nodeLevel == level { 239 // Then we want a copy of the node at this level 240 glog.V(vvLevel).Infof("copying l:%d ln:%d", level, lastNode) 241 nodeID, err := siblingIDSkipLevels(snapshot, lastNodeAtLevel, level, lastNode^1, maxBitlen) 242 if err != nil { 243 return nil, err 244 } 245 246 glog.V(vvLevel).Infof("copy node at %s", nodeID.CoordString()) 247 return append(fetches, NodeFetch{Rehash: false, NodeID: nodeID}), nil 248 } 249 250 // Left sibling and parent exist at this snapshot and don't need to be rehashed 251 glog.V(vvLevel).Infof("move up ln:%d level:%d", lastNode, level) 252 lastNode >>= 1 253 lastNodeAtLevel >>= 1 254 level++ 255 } 256 257 glog.V(vvLevel).Infof("done ln:%d level:%d", lastNode, level) 258 259 // lastNode is now the index of a left sibling with no right sibling. This is where the 260 // rehashing starts 261 savedNodeID, err := siblingIDSkipLevels(snapshot, lastNodeAtLevel, level, lastNode^1, maxBitlen) 262 glog.V(vvLevel).Infof("root for recompute is: %s", savedNodeID.CoordString()) 263 if err != nil { 264 return nil, err 265 } 266 267 if nodeLevel == level { 268 glog.V(vvLevel).Info("emit root (1)") 269 return append(fetches, NodeFetch{Rehash: true, NodeID: savedNodeID}), nil 270 } 271 272 rehash := false 273 subRootEmitted := false // whether we've added the recomputed subtree root to the path yet 274 275 // Move towards the tree root (increasing level). Exit when we reach the root or the 276 // level that is being recomputed. Defer emitting the subtree root to the path until 277 // the appropriate point because we don't immediately know whether it's part of the 278 // rehashing. 279 for lastNode != 0 { 280 glog.V(vvLevel).Infof("in loop level:%d ln:%d lnal:%d", level, lastNode, lastNodeAtLevel) 281 282 if (lastNode & 1) != 0 { 283 nodeID, err := siblingIDSkipLevels(snapshot, lastNodeAtLevel, level, (lastNode-1)^1, maxBitlen) 284 if err != nil { 285 return nil, err 286 } 287 288 if !rehash && !subRootEmitted { 289 glog.V(vvLevel).Info("emit root (2)") 290 fetches = append(fetches, NodeFetch{Rehash: true, NodeID: savedNodeID}) 291 subRootEmitted = true 292 } 293 294 glog.V(vvLevel).Infof("rehash with %s", nodeID.CoordString()) 295 fetches = append(fetches, NodeFetch{Rehash: true, NodeID: nodeID}) 296 rehash = true 297 } 298 299 lastNode >>= 1 300 lastNodeAtLevel >>= 1 301 level++ 302 303 if nodeLevel == level && !subRootEmitted { 304 glog.V(vvLevel).Info("emit root (3)") 305 return append(fetches, NodeFetch{Rehash: rehash, NodeID: savedNodeID}), nil 306 } 307 308 // Exit early if we've gone far enough up the tree to hit the level we're recomputing 309 if level == nodeLevel { 310 glog.V(vvLevel).Infof("returning fetches early: %v", fetches) 311 return fetches, nil 312 } 313 } 314 315 glog.V(vvLevel).Infof("returning fetches: %v", fetches) 316 return fetches, nil 317 } 318 319 // lastNodeWritten determines if the last node is present in storage for a given Merkle tree size 320 // and level in the tree (0 = leaves, increasing towards the root). This is determined by 321 // examining the bits of the last valid leaf index in a tree of the specified size. Zero bits 322 // indicate nodes that are not stored at that tree size. 323 // 324 // Examples, all using a tree of size 5 leaves: 325 // 326 // As depicted in RFC 6962, nodes "float" upwards. 327 // 328 // hash2 329 // / \ 330 // / \ 331 // / \ 332 // / \ 333 // / \ 334 // k i 335 // / \ | 336 // / \ e 337 // / \ | 338 // g h d4 339 // / \ / \ 340 // a b c d 341 // | | | | 342 // d0 d1 d2 d3 343 // 344 // In the C++ reference implementation, intermediate nodes are stored, leaves are at level 0. 345 // There is a dummy copy from the level below stored where the last node at a level has no right 346 // sibling. More detail is given in the comments of: 347 // https://github.com/google/certificate-transparency/blob/master/cpp/merkletree/merkle_tree.h 348 // 349 // hash2 350 // / \ 351 // / \ 352 // / \ 353 // / \ 354 // / \ 355 // k e 356 // / \ \ 357 // / \ \ 358 // / \ \ 359 // g h e 360 // / \ / \ / 361 // a b c d e 362 // | | | | | 363 // d0 d1 d2 d3 d4 364 // 365 // In our storage implementation shown in the next diagram, nodes "sink" downwards, [X] nodes 366 // with one child are not written, there is no dummy copy. Leaves are at level zero. 367 // 368 // hash2 369 // / \ 370 // / \ 371 // / \ 372 // / \ 373 // / \ 374 // k [X] Level 2 375 // / \ \ 376 // / \ \ 377 // / \ \ 378 // g h [X] Level 1 379 // / \ / \ / 380 // a b c d e Level 0 381 // | | | | | 382 // d0 d1 d2 d3 d4 383 // 384 // Tree size = 5, last index = 4 in binary = 100, append 1 for leaves = 1001. 385 // Reading down the RHS: present, not present, not present, present = 1001. So when 386 // attempting to fetch the sibling of k (level 2, index 1) the tree should be descended twice to 387 // fetch 'e' (level 0, index 4) as (level 1, index 2) is also not present in storage. 388 func lastNodePresent(level, ts int64) bool { 389 if level == 0 { 390 // Leaves always exist 391 return true 392 } 393 394 // Last index in the level is the tree size - 1 395 bits := uint64(ts - 1) 396 // Test the bit in the path for the requested level 397 mask := uint64(1) << uint64(level-1) 398 399 return bits&mask != 0 400 } 401 402 // skipMissingLevels moves down the tree a level towards the leaves until the node exists. This 403 // must terminate successfully as we will eventually reach the leaves, which are always written 404 // and are at level 0. Missing nodes are intermediate nodes with one child, hence their value 405 // is the same as the node lower down the tree as there is nothing to hash it with. 406 func skipMissingLevels(snapshot, lastNode int64, level int, node int64) (int, int64) { 407 sibling := node ^ 1 408 for level > 0 && sibling == lastNode && !lastNodePresent(int64(level), snapshot) { 409 level-- 410 sibling *= 2 411 lastNode = (snapshot - 1) >> uint(level) 412 glog.V(vvLevel).Infof("Move down: S:%d L:%d LN:%d", sibling, level, lastNode) 413 } 414 415 return level, sibling 416 } 417 418 // checkRecomputation carries out an additional check that the results of recomputePastSnapshot 419 // are valid. There must be at least one fetch. All fetches must have the same rehash state and if 420 // there is only one fetch then it must not be a rehash. If all checks pass then the fetches 421 // represent one node after rehashing is completed. 422 func checkRecomputation(fetches []NodeFetch) error { 423 switch len(fetches) { 424 case 0: 425 return errors.New("recomputePastSnapshot returned nothing") 426 case 1: 427 if fetches[0].Rehash { 428 return fmt.Errorf("recomputePastSnapshot returned invalid rehash: %v", fetches) 429 } 430 default: 431 for i := range fetches { 432 if i > 0 && fetches[i].Rehash != fetches[0].Rehash { 433 return fmt.Errorf("recomputePastSnapshot returned mismatched rehash nodes: %v", fetches) 434 } 435 } 436 } 437 438 return nil 439 } 440 441 // siblingIDSkipLevels creates a new NodeID for the supplied node, accounting for levels skipped 442 // in storage. Note that it returns an ID for the node sibling so care should be taken to pass the 443 // correct value for the node parameter. 444 func siblingIDSkipLevels(snapshot, lastNode int64, level int, node int64, maxBitLen int) (storage.NodeID, error) { 445 l, sibling := skipMissingLevels(snapshot, lastNode, level, node) 446 return storage.NewNodeIDForTreeCoords(int64(l), sibling, maxBitLen) 447 }