github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/merkle/log_verifier_test.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 merkle
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/hex"
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/google/trillian/merkle/rfc6962"
    25  )
    26  
    27  type inclusionProofTestVector struct {
    28  	leaf     int64
    29  	snapshot int64
    30  	proof    [][]byte
    31  }
    32  
    33  type consistencyTestVector struct {
    34  	snapshot1 int64
    35  	snapshot2 int64
    36  	proof     [][]byte
    37  }
    38  
    39  var (
    40  	sha256SomeHash      = dh("abacaba000000000000000000000000000000000000000000060061e00123456", 32)
    41  	sha256EmptyTreeHash = dh("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 32)
    42  
    43  	inclusionProofs = []inclusionProofTestVector{
    44  		{0, 0, nil},
    45  		{1, 1, nil},
    46  		{1, 8, [][]byte{
    47  			dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32),
    48  			dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32),
    49  			dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32)}},
    50  		{6, 8, [][]byte{
    51  			dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32),
    52  			dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32),
    53  			dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32)}},
    54  		{3, 3, [][]byte{
    55  			dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32)}},
    56  		{2, 5, [][]byte{
    57  			dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32),
    58  			dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32),
    59  			dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32)}},
    60  	}
    61  
    62  	consistencyProofs = []consistencyTestVector{
    63  		{1, 1, nil},
    64  		{1, 8, [][]byte{
    65  			dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32),
    66  			dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32),
    67  			dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32)}},
    68  		{6, 8, [][]byte{
    69  			dh("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", 32),
    70  			dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32),
    71  			dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32)}},
    72  		{2, 5, [][]byte{
    73  			dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32),
    74  			dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32)}},
    75  	}
    76  
    77  	roots = [][]byte{
    78  		dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32),
    79  		dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32),
    80  		dh("aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", 32),
    81  		dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32),
    82  		dh("4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", 32),
    83  		dh("76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", 32),
    84  		dh("ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", 32),
    85  		dh("5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", 32),
    86  	}
    87  
    88  	leaves = [][]byte{
    89  		dh("", 0),
    90  		dh("00", 1),
    91  		dh("10", 1),
    92  		dh("2021", 2),
    93  		dh("3031", 2),
    94  		dh("40414243", 4),
    95  		dh("5051525354555657", 8),
    96  		dh("606162636465666768696a6b6c6d6e6f", 16),
    97  	}
    98  )
    99  
   100  // inclusionProbe is a parameter set for inclusion proof verification.
   101  type inclusionProbe struct {
   102  	leafIndex int64
   103  	treeSize  int64
   104  	root      []byte
   105  	leafHash  []byte
   106  	proof     [][]byte
   107  
   108  	desc string
   109  }
   110  
   111  // consistencyProbe is a parameter set for consistency proof verification.
   112  type consistencyProbe struct {
   113  	snapshot1 int64
   114  	snapshot2 int64
   115  	root1     []byte
   116  	root2     []byte
   117  	proof     [][]byte
   118  
   119  	desc string
   120  }
   121  
   122  func corruptInclusionProof(leafIndex, treeSize int64, proof [][]byte, root, leafHash []byte) []inclusionProbe {
   123  	ret := []inclusionProbe{
   124  		// Wrong leaf index.
   125  		{leafIndex - 1, treeSize, root, leafHash, proof, "leafIndex - 1"},
   126  		{leafIndex + 1, treeSize, root, leafHash, proof, "leafIndex + 1"},
   127  		{leafIndex ^ 2, treeSize, root, leafHash, proof, "leafIndex ^ 2"},
   128  		// Wrong tree height.
   129  		{leafIndex, treeSize * 2, root, leafHash, proof, "treeSize * 2"},
   130  		{leafIndex, treeSize / 2, root, leafHash, proof, "treeSize / 2"},
   131  		// Wrong leaf or root.
   132  		{leafIndex, treeSize, root, []byte("WrongLeaf"), proof, "wrong leaf"},
   133  		{leafIndex, treeSize, sha256EmptyTreeHash, leafHash, proof, "empty root"},
   134  		{leafIndex, treeSize, sha256SomeHash, leafHash, proof, "random root"},
   135  		// Add garbage at the end.
   136  		{leafIndex, treeSize, root, leafHash, extend(proof, []byte{}), "trailing garbage"},
   137  		{leafIndex, treeSize, root, leafHash, extend(proof, root), "trailing root"},
   138  		// Add garbage at the front.
   139  		{leafIndex, treeSize, root, leafHash, prepend(proof, []byte{}), "preceding garbage"},
   140  		{leafIndex, treeSize, root, leafHash, prepend(proof, root), "preceding root"},
   141  	}
   142  	ln := len(proof)
   143  
   144  	// Modify single bit in an element of the proof.
   145  	for i := 0; i < ln; i++ {
   146  		wrongProof := prepend(proof)                          // Copy the proof slice.
   147  		wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data.
   148  		wrongProof[i][0] ^= 8                                 // Flip the bit.
   149  		desc := fmt.Sprintf("modified proof[%d] bit 3", i)
   150  		ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, desc})
   151  	}
   152  
   153  	if ln > 0 {
   154  		ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, proof[:ln-1], "removed component"})
   155  	}
   156  	if ln > 1 {
   157  		wrongProof := prepend(proof[1:], proof[0], sha256SomeHash)
   158  		ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, "inserted component"})
   159  	}
   160  
   161  	return ret
   162  }
   163  
   164  func corruptConsistencyProof(snapshot1, snapshot2 int64, root1, root2 []byte, proof [][]byte) []consistencyProbe {
   165  	ln := len(proof)
   166  	ret := []consistencyProbe{
   167  		// Wrong snapshot index.
   168  		{snapshot1 - 1, snapshot2, root1, root2, proof, "snapshot1 - 1"},
   169  		{snapshot1 + 1, snapshot2, root1, root2, proof, "snapshot1 + 1"},
   170  		{snapshot1 ^ 2, snapshot2, root1, root2, proof, "snapshot1 ^ 2"},
   171  		// Wrong tree height.
   172  		{snapshot1, snapshot2 * 2, root1, root2, proof, "snapshot2 * 2"},
   173  		{snapshot1, snapshot2 / 2, root1, root2, proof, "snapshot2 / 2"},
   174  		// Wrong root.
   175  		{snapshot1, snapshot2, []byte("WrongRoot"), root2, proof, "wrong root1"},
   176  		{snapshot1, snapshot2, root1, []byte("WrongRoot"), proof, "wrong root2"},
   177  		{snapshot1, snapshot2, root2, root1, proof, "swapped roots"},
   178  		// Empty proof.
   179  		{snapshot1, snapshot2, root1, root2, [][]byte{}, "empty proof"},
   180  		// Add garbage at the end.
   181  		{snapshot1, snapshot2, root1, root2, extend(proof, []byte{}), "trailing garbage"},
   182  		{snapshot1, snapshot2, root1, root2, extend(proof, root1), "trailing root1"},
   183  		{snapshot1, snapshot2, root1, root2, extend(proof, root2), "trailing root2"},
   184  		// Add garbage at the front.
   185  		{snapshot1, snapshot2, root1, root2, prepend(proof, []byte{}), "preceding garbage"},
   186  		{snapshot1, snapshot2, root1, root2, prepend(proof, root1), "preceding root1"},
   187  		{snapshot1, snapshot2, root1, root2, prepend(proof, root2), "preceding root2"},
   188  		{snapshot1, snapshot2, root1, root2, prepend(proof, proof[0]), "preceding proof[0]"},
   189  	}
   190  
   191  	// Remove a node from the end.
   192  	if ln > 0 {
   193  		ret = append(ret, consistencyProbe{snapshot1, snapshot2, root1, root2, proof[:ln-1], "truncated proof"})
   194  	}
   195  
   196  	// Modify single bit in an element of the proof.
   197  	for i := 0; i < ln; i++ {
   198  		wrongProof := prepend(proof)                          // Copy the proof slice.
   199  		wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data.
   200  		wrongProof[i][0] ^= 16                                // Flip the bit.
   201  		desc := fmt.Sprintf("modified proof[%d] bit 4", i)
   202  		ret = append(ret, consistencyProbe{snapshot1, snapshot2, root1, root2, wrongProof, desc})
   203  	}
   204  
   205  	return ret
   206  }
   207  
   208  func verifierCheck(v *LogVerifier, leafIndex, treeSize int64, proof [][]byte, root, leafHash []byte) error {
   209  	// Verify original inclusion proof.
   210  	got, err := v.RootFromInclusionProof(leafIndex, treeSize, proof, leafHash)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	if !bytes.Equal(got, root) {
   215  		return fmt.Errorf("got root:\n%x\nexpected:\n%x", got, root)
   216  	}
   217  	if err := v.VerifyInclusionProof(leafIndex, treeSize, proof, root, leafHash); err != nil {
   218  		return err
   219  	}
   220  
   221  	probes := corruptInclusionProof(leafIndex, treeSize, proof, root, leafHash)
   222  	var wrong []string
   223  	for _, p := range probes {
   224  		if err := v.VerifyInclusionProof(p.leafIndex, p.treeSize, p.proof, p.root, p.leafHash); err == nil {
   225  			wrong = append(wrong, p.desc)
   226  		}
   227  	}
   228  	if len(wrong) > 0 {
   229  		return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", "))
   230  	}
   231  	return nil
   232  }
   233  
   234  func verifierConsistencyCheck(v *LogVerifier, snapshot1, snapshot2 int64, root1, root2 []byte, proof [][]byte) error {
   235  	// Verify original consistency proof.
   236  	if err := v.VerifyConsistencyProof(snapshot1, snapshot2, root1, root2, proof); err != nil {
   237  		return err
   238  	}
   239  	// For simplicity test only non-trivial proofs that have root1 != root2,
   240  	// snapshot1 != 0 and snapshot1 != snapshot2.
   241  	if len(proof) == 0 {
   242  		return nil
   243  	}
   244  
   245  	probes := corruptConsistencyProof(snapshot1, snapshot2, root1, root2, proof)
   246  	var wrong []string
   247  	for _, p := range probes {
   248  		if err := v.VerifyConsistencyProof(p.snapshot1, p.snapshot2, p.root1, p.root2, p.proof); err == nil {
   249  			wrong = append(wrong, p.desc)
   250  		}
   251  	}
   252  	if len(wrong) > 0 {
   253  		return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", "))
   254  	}
   255  	return nil
   256  }
   257  
   258  func TestVerifyInclusionProofSingleEntry(t *testing.T) {
   259  	v := NewLogVerifier(rfc6962.DefaultHasher)
   260  	data := []byte("data")
   261  	// Root and leaf hash for 1-entry tree are the same.
   262  	hash, _ := v.hasher.HashLeaf(data)
   263  	// The corresponding inclusion proof is empty.
   264  	proof := [][]byte{}
   265  	emptyHash := []byte{}
   266  
   267  	for i, tc := range []struct {
   268  		root    []byte
   269  		leaf    []byte
   270  		wantErr bool
   271  	}{
   272  		{hash, hash, false},
   273  		{hash, emptyHash, true},
   274  		{emptyHash, hash, true},
   275  		{emptyHash, emptyHash, true}, // Wrong hash size.
   276  	} {
   277  		t.Run(fmt.Sprintf("test:%d", i), func(t *testing.T) {
   278  			err := v.VerifyInclusionProof(0, 1, proof, tc.root, tc.leaf)
   279  			if got, want := (err != nil), tc.wantErr; got != want {
   280  				t.Errorf("error: %v, want %v", got, want)
   281  			}
   282  		})
   283  	}
   284  }
   285  
   286  func TestVerifyInclusionProof(t *testing.T) {
   287  	v := NewLogVerifier(rfc6962.DefaultHasher)
   288  	proof := [][]byte{}
   289  
   290  	probes := []struct {
   291  		index, size int64
   292  	}{{0, 0}, {0, 1}, {1, 0}, {2, 1}}
   293  	for _, p := range probes {
   294  		t.Run(fmt.Sprintf("probe:%d:%d", p.index, p.size), func(t *testing.T) {
   295  			if err := v.VerifyInclusionProof(p.index, p.size, proof, []byte{}, sha256SomeHash); err == nil {
   296  				t.Error("Incorrectly verified invalid root/leaf")
   297  			}
   298  			if err := v.VerifyInclusionProof(p.index, p.size, proof, sha256EmptyTreeHash, []byte{}); err == nil {
   299  				t.Error("Incorrectly verified invalid root/leaf")
   300  			}
   301  			if err := v.VerifyInclusionProof(p.index, p.size, proof, sha256EmptyTreeHash, sha256SomeHash); err == nil {
   302  				t.Error("Incorrectly verified invalid root/leaf")
   303  			}
   304  		})
   305  	}
   306  
   307  	// i = 0 is an invalid path.
   308  	for i := 1; i < 6; i++ {
   309  		p := inclusionProofs[i]
   310  		t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) {
   311  			leafHash, err := rfc6962.DefaultHasher.HashLeaf(leaves[p.leaf-1])
   312  			if err != nil {
   313  				t.Fatalf("HashLeaf(): %v", err)
   314  			}
   315  			if err := verifierCheck(&v, p.leaf-1, p.snapshot, p.proof, roots[p.snapshot-1], leafHash); err != nil {
   316  				t.Errorf("verifierCheck(): %s", err)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestVerifyInclusionProofGenerated(t *testing.T) {
   323  	var sizes []int64
   324  	for s := 1; s <= 70; s++ {
   325  		sizes = append(sizes, int64(s))
   326  	}
   327  	sizes = append(sizes, []int64{1024, 5050}...)
   328  
   329  	tree, v := createTree(0)
   330  	for _, size := range sizes {
   331  		growTree(tree, size)
   332  		root := tree.CurrentRoot().Hash()
   333  		for i := int64(0); i < size; i++ {
   334  			t.Run(fmt.Sprintf("size:%d:index:%d", size, i), func(t *testing.T) {
   335  				leaf, proof := getLeafAndProof(tree, i)
   336  				if err := verifierCheck(&v, i, size, proof, root, leaf); err != nil {
   337  					t.Errorf("verifierCheck(): %v", err)
   338  				}
   339  			})
   340  		}
   341  	}
   342  }
   343  
   344  func TestVerifyConsistencyProof(t *testing.T) {
   345  	v := NewLogVerifier(rfc6962.DefaultHasher)
   346  
   347  	root1 := []byte("don't care 1")
   348  	root2 := []byte("don't care 2")
   349  	proof1 := [][]byte{}
   350  	proof2 := [][]byte{sha256EmptyTreeHash}
   351  
   352  	tests := []struct {
   353  		snap1, snap2 int64
   354  		root1, root2 []byte
   355  		proof        [][]byte
   356  		wantErr      bool
   357  	}{
   358  		{0, 0, root1, root2, proof1, true},
   359  		{1, 1, root1, root2, proof1, true},
   360  		// Snapshots that are always consistent.
   361  		{0, 0, root1, root1, proof1, false},
   362  		{0, 1, root1, root2, proof1, false},
   363  		{1, 1, root2, root2, proof1, false},
   364  		// Time travel to the past.
   365  		{1, 0, root1, root2, proof1, true},
   366  		{2, 1, root1, root2, proof1, true},
   367  		// Empty proof.
   368  		{1, 2, root1, root2, proof1, true},
   369  		// Roots don't match.
   370  		{0, 0, sha256EmptyTreeHash, root2, proof1, true},
   371  		{1, 1, sha256EmptyTreeHash, root2, proof1, true},
   372  		// Roots match but the proof is not empty.
   373  		{0, 0, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true},
   374  		{0, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true},
   375  		{1, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true},
   376  	}
   377  	for i, p := range tests {
   378  		t.Run(fmt.Sprintf("test:%d:snap:%d-%d", i, p.snap1, p.snap2), func(t *testing.T) {
   379  			err := verifierConsistencyCheck(&v, p.snap1, p.snap2, p.root1, p.root2, p.proof)
   380  			if p.wantErr && err == nil {
   381  				t.Errorf("Incorrectly verified")
   382  			} else if !p.wantErr && err != nil {
   383  				t.Errorf("Failed to verify: %v", err)
   384  			}
   385  		})
   386  	}
   387  
   388  	for i := 0; i < 4; i++ {
   389  		p := consistencyProofs[i]
   390  		t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) {
   391  			err := verifierConsistencyCheck(&v, p.snapshot1, p.snapshot2,
   392  				roots[p.snapshot1-1], roots[p.snapshot2-1], p.proof)
   393  			if err != nil {
   394  				t.Fatalf("Failed to verify known good proof: %s", err)
   395  			}
   396  		})
   397  	}
   398  }
   399  
   400  func TestVerifyConsistencyProofGenerated(t *testing.T) {
   401  	size := int64(130)
   402  	tree, v := createTree(size)
   403  	roots := make([][]byte, size+1)
   404  	for i := int64(0); i <= size; i++ {
   405  		roots[i] = tree.RootAtSnapshot(i).Hash()
   406  	}
   407  
   408  	for i := int64(0); i <= size; i++ {
   409  		for j := i; j <= size; j++ {
   410  			proof := rawProof(tree.SnapshotConsistency(i, j))
   411  			t.Run(fmt.Sprintf("size:%d:consistency:%d-%d", size, i, j), func(t *testing.T) {
   412  				if err := verifierConsistencyCheck(&v, i, j, roots[i], roots[j], proof); err != nil {
   413  					t.Errorf("verifierConsistencyCheck(): %v", err)
   414  				}
   415  			})
   416  		}
   417  	}
   418  }
   419  
   420  func TestPrefixHashFromInclusionProofGenerated(t *testing.T) {
   421  	var sizes []int64
   422  	for s := 1; s <= 258; s++ {
   423  		sizes = append(sizes, int64(s))
   424  	}
   425  	sizes = append(sizes, []int64{1024, 5050, 10000}...)
   426  
   427  	tree, v := createTree(0)
   428  	for _, size := range sizes {
   429  		growTree(tree, size)
   430  		root := tree.CurrentRoot().Hash()
   431  
   432  		for i := int64(1); i <= size; i++ {
   433  			t.Run(fmt.Sprintf("size:%d:prefix:%d", size, i), func(t *testing.T) {
   434  				leaf, proof := getLeafAndProof(tree, i-1)
   435  				pRoot, err := v.VerifiedPrefixHashFromInclusionProof(i, size, proof, root, leaf)
   436  				if err != nil {
   437  					t.Fatalf("VerifiedPrefixHashFromInclusionProof(): %v", err)
   438  				}
   439  				exp := tree.RootAtSnapshot(i).Hash()
   440  				if !bytes.Equal(pRoot, exp) {
   441  					t.Fatalf("wrong prefix hash: %s, want %s", shortHash(pRoot), shortHash(exp))
   442  				}
   443  			})
   444  		}
   445  	}
   446  }
   447  
   448  func TestPrefixHashFromInclusionProofErrors(t *testing.T) {
   449  	size := int64(307)
   450  	tree, v := createTree(size)
   451  	root := tree.CurrentRoot().Hash()
   452  
   453  	leaf2, proof2 := getLeafAndProof(tree, 2)
   454  	_, proof3 := getLeafAndProof(tree, 3)
   455  	_, proof301 := getLeafAndProof(tree, 301)
   456  
   457  	idxTests := []struct {
   458  		index int64
   459  		size  int64
   460  	}{
   461  		{-1, -1}, {-10, -1}, {-1, -10},
   462  		{10, -1}, {10, 0}, {10, 9}, {0, 10},
   463  		{0, -1}, {0, 0}, {-1, 0},
   464  		{-1, size}, {0, size}, {size, size}, {size + 1, size}, {size + 100, size},
   465  	}
   466  	for _, it := range idxTests {
   467  		if _, err := v.VerifiedPrefixHashFromInclusionProof(it.index, it.size, proof2, root, leaf2); err == nil {
   468  			t.Errorf("VerifiedPrefixHashFromInclusionProof(%d,%d): expected error", it.index, it.size)
   469  		}
   470  	}
   471  
   472  	if _, err := v.VerifiedPrefixHashFromInclusionProof(3, size, proof2, root, leaf2); err != nil {
   473  		t.Errorf("VerifiedPrefixHashFromInclusionProof(): %v, expected no error", err)
   474  	}
   475  
   476  	// Proof #3 has the same length, but doesn't verify against index #2.
   477  	// Neither does proof #301 as it has a different length.
   478  	for _, proof := range [][][]byte{proof3, proof301} {
   479  		if _, err := v.VerifiedPrefixHashFromInclusionProof(3, size, proof, root, leaf2); err == nil {
   480  			t.Error("VerifiedPrefixHashFromInclusionProof(): expected error")
   481  		}
   482  	}
   483  }
   484  
   485  // extend explicitly copies |proof| slice and appends |hashes| to it.
   486  func extend(proof [][]byte, hashes ...[]byte) [][]byte {
   487  	res := make([][]byte, len(proof), len(proof)+len(hashes))
   488  	copy(res, proof)
   489  	return append(res, hashes...)
   490  }
   491  
   492  // prepend adds |proof| to the tail of |hashes|.
   493  func prepend(proof [][]byte, hashes ...[]byte) [][]byte {
   494  	return append(hashes, proof...)
   495  }
   496  
   497  func dh(h string, expLen int) []byte {
   498  	r, err := hex.DecodeString(h)
   499  	if err != nil {
   500  		panic(err)
   501  	}
   502  	if got := len(r); got != expLen {
   503  		panic(fmt.Sprintf("decode %q: len=%d, want %d", h, got, expLen))
   504  	}
   505  	return r
   506  }
   507  
   508  func shortHash(hash []byte) string {
   509  	if len(hash) == 0 {
   510  		return "<empty>"
   511  	}
   512  	return fmt.Sprintf("%x...", hash[:4])
   513  }
   514  
   515  func createTree(size int64) (*InMemoryMerkleTree, LogVerifier) {
   516  	tree := NewInMemoryMerkleTree(rfc6962.DefaultHasher)
   517  	growTree(tree, size)
   518  	return tree, NewLogVerifier(rfc6962.DefaultHasher)
   519  }
   520  
   521  func growTree(tree *InMemoryMerkleTree, upTo int64) {
   522  	for i := tree.LeafCount(); i < upTo; i++ {
   523  		data := []byte(fmt.Sprintf("data:%d", i))
   524  		tree.AddLeaf(data)
   525  	}
   526  }
   527  
   528  func getLeafAndProof(tree *InMemoryMerkleTree, index int64) ([]byte, [][]byte) {
   529  	// Note: InMemoryMerkleTree counts leaves from 1.
   530  	proof := rawProof(tree.PathToCurrentRoot(index + 1))
   531  	leafHash := tree.LeafHash(index + 1)
   532  	return leafHash, proof
   533  }
   534  
   535  func rawProof(desc []TreeEntryDescriptor) [][]byte {
   536  	proof := make([][]byte, len(desc))
   537  	for i, d := range desc {
   538  		proof[i] = d.Value.Hash()
   539  	}
   540  	return proof
   541  }