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  }