Skip to content
This repository was archived by the owner on Oct 11, 2024. It is now read-only.

Commit 2152cd4

Browse files
committed
WIP: reintroduce verification
1 parent f881945 commit 2152cd4

File tree

2 files changed

+172
-5
lines changed

2 files changed

+172
-5
lines changed

core/monitor/monitor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ type Monitor struct {
3939
logVerifier merkle.LogVerifier
4040
signer *tcrypto.Signer
4141
// TODO(ismail): update last trusted signed log root
42-
//trusted trillian.SignedLogRoot
43-
store *storage.Storage
42+
trusted *trillian.SignedLogRoot
43+
store *storage.Storage
4444
}
4545

4646
// New creates a new instance of the monitor.

core/monitor/verify.go

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,181 @@
1818
package monitor
1919

2020
import (
21+
"bytes"
22+
"errors"
23+
"math/big"
24+
25+
"github.com/golang/glog"
26+
27+
"github.com/google/trillian/merkle"
28+
"github.com/google/trillian/storage"
29+
30+
tcrypto "github.com/google/trillian/crypto"
31+
32+
"github.com/google/keytransparency/core/mutator/entry"
2133
ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types"
2234
)
2335

24-
// verifyMutationsResponse verifies a response received by the GetMutations API.
36+
var (
37+
// ErrInvalidMutation occurs when verification failed because of an invalid
38+
// mutation.
39+
ErrInvalidMutation = errors.New("invalid mutation")
40+
// ErrNotMatchingMapRoot occurs when the reconstructed root differs from the one
41+
// we received from the server.
42+
ErrNotMatchingMapRoot = errors.New("recreated root does not match")
43+
// ErrInvalidMapSignature occurs if the map roots signature does not verify.
44+
ErrInvalidMapSignature = errors.New("invalid signature on map root")
45+
// ErrInvalidLogSignature occurs if the log roots signature does not verify.
46+
ErrInvalidLogSignature = errors.New("invalid signature on log root")
47+
// ErrInconsistentProofs occurs when the server returned different hashes
48+
// for the same inclusion proof node in the tree.
49+
ErrInconsistentProofs = errors.New("inconsistent inclusion proofs")
50+
// ErrInvalidConsistencyProof occurs when the log consistency proof does not
51+
// verify.
52+
ErrInvalidConsistencyProof = errors.New("invalid log consistency proof")
53+
)
54+
55+
// verifyResponse verifies a response received by the GetMutations API.
2556
// Additionally to the response it takes a complete list of mutations. The list
2657
// of received mutations may differ from those included in the initial response
27-
// because of the max. page size. If any verification check failed it returns
28-
// an error.
58+
// because of the max. page size.
2959
func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error {
60+
errList := make([]error, 0)
61+
// copy of singed map root
62+
smr := *in.GetSmr()
63+
// reset to the state before it was signed:
64+
smr.Signature = nil
65+
// verify signature on map root:
66+
if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil {
67+
glog.Infof("couldn't verify signature on map root: %v", err)
68+
errList = append(errList, ErrInvalidMapSignature)
69+
}
70+
71+
logRoot := in.GetLogRoot()
72+
// Verify SignedLogRoot signature.
73+
hash := tcrypto.HashLogRoot(*logRoot)
74+
if err := tcrypto.Verify(m.logPubKey, hash, logRoot.GetSignature()); err != nil {
75+
glog.Infof("couldn't verify signature on log root: %v", err)
76+
errList = append(errList, ErrInvalidLogSignature)
77+
}
78+
79+
if m.trusted != nil {
80+
// Verify consistency proof:
81+
err := m.logVerifier.VerifyConsistencyProof(
82+
m.trusted.TreeSize, logRoot.TreeSize,
83+
m.trusted.RootHash, logRoot.RootHash,
84+
in.GetLogConsistency())
85+
if err != nil {
86+
errList = append(errList, ErrInvalidConsistencyProof)
87+
}
88+
} else {
89+
// trust the first log root we see, don't verify anything yet
90+
m.trusted = in.GetLogRoot()
91+
}
92+
93+
// m.logVerifier.VerifyInclusionProof()
94+
//
95+
// retrieve the old root hash from storage!
96+
monRes, err := m.store.Get(in.Epoch - 1)
97+
if err != nil {
98+
glog.Infof("Could not retrieve previous monitoring result: %v", err)
99+
}
100+
// we need the old root for verifying the inclusion of the old leafs in the
101+
// previous epoch. Storage always stores the mutations response independent
102+
// from if the checks succeeded or not.
103+
oldRoot := monRes.Response.GetSmr().GetRootHash()
104+
105+
if err := m.verifyMutations(in.GetMutations(), oldRoot,
106+
in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 {
107+
errList = append(errList, err...)
108+
}
109+
110+
return errList
111+
}
112+
113+
func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoot []byte, mapID int64) []error {
114+
errList := make([]error, 0)
115+
mutator := entry.New()
116+
oldProofNodes := make(map[string][]byte)
117+
newLeaves := make([]merkle.HStar2LeafHash, 0, len(muts))
118+
119+
for _, mut := range muts {
120+
oldLeaf, err := entry.FromLeafValue(mut.GetProof().GetLeaf().GetLeafValue())
121+
if err != nil {
122+
errList = append(errList, ErrInvalidMutation)
123+
}
124+
125+
// verify that the provided leaf’s inclusion proof goes to epoch e-1:
126+
index := mut.GetProof().GetLeaf().GetIndex()
127+
leafHash := mut.GetProof().GetLeaf().GetLeafHash()
128+
if err := merkle.VerifyMapInclusionProof(mapID, index,
129+
leafHash, oldRoot, mut.GetProof().GetInclusion(), m.hasher); err != nil {
130+
glog.Infof("VerifyMapInclusionProof(%x): %v", index, err)
131+
errList = append(errList, err)
132+
}
133+
134+
// compute the new leaf
135+
newLeaf, err := mutator.Mutate(oldLeaf, mut.GetUpdate())
136+
if err != nil {
137+
errList = append(errList, ErrInvalidMutation)
138+
}
139+
newLeafnID := storage.NewNodeIDFromPrefixSuffix(index, storage.Suffix{}, m.hasher.BitLen())
140+
newLeafHash := m.hasher.HashLeaf(mapID, index, newLeaf)
141+
newLeaves = append(newLeaves, merkle.HStar2LeafHash{
142+
Index: newLeafnID.BigInt(),
143+
LeafHash: newLeafHash,
144+
})
145+
146+
// store the proof hashes locally to recompute the tree below:
147+
sibIDs := newLeafnID.Siblings()
148+
// TODO(ismail) iterate over the sibIDs instead!
149+
for level, proof := range mut.GetProof().GetInclusion() {
150+
pID := sibIDs[level]
151+
if p, ok := oldProofNodes[pID.String()]; ok {
152+
// sanity check: for each mut overlapping proof nodes should be
153+
// equal:
154+
if !bytes.Equal(p, proof) {
155+
// TODO(ismail): remove this check and this error type as
156+
// soon as the server does not return interior proof nodes
157+
// multiple times
158+
//
159+
// this is really odd and should never happen
160+
errList = append(errList, ErrInconsistentProofs)
161+
}
162+
} else {
163+
oldProofNodes[pID.String()] = proof
164+
}
165+
}
166+
}
167+
if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil {
168+
errList = append(errList, err)
169+
}
170+
171+
return errList
172+
}
173+
174+
func (m *Monitor) validateMapRoot(expectedRoot []byte, mapID int64, mutatedLeaves []merkle.HStar2LeafHash, oldProofNodes map[string][]byte) error {
175+
// compute the new root using local intermediate hashes from epoch e
176+
// (above proof hashes):
177+
hs2 := merkle.NewHStar2(mapID, m.hasher)
178+
newRoot, err := hs2.HStar2Nodes([]byte{}, m.hasher.Size(), mutatedLeaves,
179+
func(depth int, index *big.Int) ([]byte, error) {
180+
nID := storage.NewNodeIDFromBigInt(depth, index, m.hasher.BitLen())
181+
if p, ok := oldProofNodes[nID.String()]; ok {
182+
return p, nil
183+
}
184+
return nil, nil
185+
}, nil)
186+
187+
if err != nil {
188+
glog.Errorf("hs2.HStar2Nodes(_): %v", err)
189+
// TODO(ismail): figure out what to return here
190+
}
191+
192+
// verify rootHash
193+
if !bytes.Equal(newRoot, expectedRoot) {
194+
return ErrNotMatchingMapRoot
195+
}
196+
30197
return nil
31198
}

0 commit comments

Comments
 (0)