github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/merkle/log_verifier.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 "errors" 20 "fmt" 21 "math/bits" 22 23 "github.com/google/trillian/merkle/hashers" 24 ) 25 26 // RootMismatchError occurs when an inclusion proof fails. 27 type RootMismatchError struct { 28 ExpectedRoot []byte 29 CalculatedRoot []byte 30 } 31 32 func (e RootMismatchError) Error() string { 33 return fmt.Sprintf("calculated root:\n%v\n does not match expected root:\n%v", e.CalculatedRoot, e.ExpectedRoot) 34 } 35 36 // LogVerifier verifies inclusion and consistency proofs for append only logs. 37 type LogVerifier struct { 38 hasher hashers.LogHasher 39 } 40 41 // NewLogVerifier returns a new LogVerifier for a tree. 42 func NewLogVerifier(hasher hashers.LogHasher) LogVerifier { 43 return LogVerifier{hasher} 44 } 45 46 // VerifyInclusionProof verifies the correctness of the proof given the passed 47 // in information about the tree and leaf. 48 func (v LogVerifier) VerifyInclusionProof(leafIndex, treeSize int64, proof [][]byte, root []byte, leafHash []byte) error { 49 calcRoot, err := v.RootFromInclusionProof(leafIndex, treeSize, proof, leafHash) 50 if err != nil { 51 return err 52 } 53 if !bytes.Equal(calcRoot, root) { 54 return RootMismatchError{ 55 CalculatedRoot: calcRoot, 56 ExpectedRoot: root, 57 } 58 } 59 return nil 60 } 61 62 // RootFromInclusionProof calculates the expected tree root given the proof and leaf. 63 // leafIndex starts at 0. treeSize is the number of nodes in the tree. 64 // proof is an array of neighbor nodes from the bottom to the root. 65 func (v LogVerifier) RootFromInclusionProof(leafIndex, treeSize int64, proof [][]byte, leafHash []byte) ([]byte, error) { 66 switch { 67 case leafIndex < 0: 68 return nil, fmt.Errorf("leafIndex %d < 0", leafIndex) 69 case treeSize < 0: 70 return nil, fmt.Errorf("treeSize %d < 0", treeSize) 71 case leafIndex >= treeSize: 72 return nil, fmt.Errorf("leafIndex is beyond treeSize: %d >= %d", leafIndex, treeSize) 73 } 74 if got, want := len(leafHash), v.hasher.Size(); got != want { 75 return nil, fmt.Errorf("leafHash has unexpected size %d, want %d", got, want) 76 } 77 78 inner, border := decompInclProof(leafIndex, treeSize) 79 if got, want := len(proof), inner+border; got != want { 80 return nil, fmt.Errorf("wrong proof size %d, want %d", got, want) 81 } 82 83 ch := hashChainer(v) 84 res := ch.chainInner(leafHash, proof[:inner], leafIndex) 85 res = ch.chainBorderRight(res, proof[inner:]) 86 return res, nil 87 } 88 89 // VerifyConsistencyProof checks that the passed in consistency proof is valid 90 // between the passed in tree snapshots. Snapshots are the respective tree 91 // sizes. Accepts shapshot2 >= snapshot1 >= 0. 92 func (v LogVerifier) VerifyConsistencyProof(snapshot1, snapshot2 int64, root1, root2 []byte, proof [][]byte) error { 93 switch { 94 case snapshot1 < 0: 95 return fmt.Errorf("snapshot1 (%d) < 0 ", snapshot1) 96 case snapshot2 < snapshot1: 97 return fmt.Errorf("snapshot2 (%d) < snapshot1 (%d)", snapshot1, snapshot2) 98 case snapshot1 == snapshot2: 99 if !bytes.Equal(root1, root2) { 100 return RootMismatchError{ 101 CalculatedRoot: root1, 102 ExpectedRoot: root2, 103 } 104 } else if len(proof) > 0 { 105 return errors.New("root1 and root2 match, but proof is non-empty") 106 } 107 return nil // Proof OK. 108 case snapshot1 == 0: 109 // Any snapshot greater than 0 is consistent with snapshot 0. 110 if len(proof) > 0 { 111 return fmt.Errorf("expected empty proof, but got %d components", len(proof)) 112 } 113 return nil // Proof OK. 114 case len(proof) == 0: 115 return errors.New("empty proof") 116 } 117 118 inner, border := decompInclProof(snapshot1-1, snapshot2) 119 shift := bits.TrailingZeros64(uint64(snapshot1)) 120 inner -= shift // Note: shift < inner if snapshot1 < snapshot2. 121 122 // The proof includes the root hash for the sub-tree of size 2^shift. 123 seed, start := proof[0], 1 124 if snapshot1 == 1<<uint(shift) { // Unless snapshot1 is that very 2^shift. 125 seed, start = root1, 0 126 } 127 if got, want := len(proof), start+inner+border; got != want { 128 return fmt.Errorf("wrong proof size %d, want %d", got, want) 129 } 130 proof = proof[start:] 131 // Now len(proof) == inner+border, and proof is effectively a suffix of 132 // inclusion proof for entry |snapshot1-1| in a tree of size |snapshot2|. 133 134 // Verify the first root. 135 ch := hashChainer(v) 136 mask := (snapshot1 - 1) >> uint(shift) // Start chaining from level |shift|. 137 hash1 := ch.chainInnerRight(seed, proof[:inner], mask) 138 hash1 = ch.chainBorderRight(hash1, proof[inner:]) 139 if !bytes.Equal(hash1, root1) { 140 return RootMismatchError{ 141 CalculatedRoot: hash1, 142 ExpectedRoot: root1, 143 } 144 } 145 146 // Verify the second root. 147 hash2 := ch.chainInner(seed, proof[:inner], mask) 148 hash2 = ch.chainBorderRight(hash2, proof[inner:]) 149 if !bytes.Equal(hash2, root2) { 150 return RootMismatchError{ 151 CalculatedRoot: hash2, 152 ExpectedRoot: root2, 153 } 154 } 155 156 return nil // Proof OK. 157 } 158 159 // VerifiedPrefixHashFromInclusionProof calculates a root hash over leaves 160 // [0..subSize), based on the inclusion |proof| and |leafHash| for a leaf at 161 // index |subSize-1| in a tree of the specified |size| with the passed in 162 // |root| hash. 163 // Returns an error if the |proof| verification fails. The resulting smaller 164 // tree's root hash is trusted iff the bigger tree's |root| hash is trusted. 165 func (v LogVerifier) VerifiedPrefixHashFromInclusionProof( 166 subSize, size int64, 167 proof [][]byte, root []byte, leafHash []byte, 168 ) ([]byte, error) { 169 if subSize <= 0 { 170 return nil, fmt.Errorf("subtree size is %d, want > 0", subSize) 171 } 172 leaf := subSize - 1 173 if err := v.VerifyInclusionProof(leaf, size, proof, root, leafHash); err != nil { 174 return nil, err 175 } 176 177 inner := innerProofSize(leaf, size) 178 ch := hashChainer(v) 179 res := ch.chainInnerRight(leafHash, proof[:inner], leaf) 180 res = ch.chainBorderRight(res, proof[inner:]) 181 return res, nil 182 } 183 184 // decompInclProof breaks down inclusion proof for a leaf at the specified 185 // |index| in a tree of the specified |size| into 2 components. The splitting 186 // point between them is where paths to leaves |index| and |size-1| diverge. 187 // Returns lengths of the bottom and upper proof parts correspondingly. The sum 188 // of the two determines the correct length of the inclusion proof. 189 func decompInclProof(index, size int64) (int, int) { 190 inner := innerProofSize(index, size) 191 border := bits.OnesCount64(uint64(index) >> uint(inner)) 192 return inner, border 193 } 194 func innerProofSize(index, size int64) int { 195 return bits.Len64(uint64(index ^ (size - 1))) 196 }