From 7f1f60873978b7cc8e939b35f5394720059aaf19 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Wed, 23 Aug 2017 18:58:27 -0400 Subject: [PATCH 01/10] WIP: reintroduce verification --- core/monitor/monitor.go | 4 +- core/monitor/verify.go | 173 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 5 deletions(-) diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go index 419936b28..72c941bcc 100644 --- a/core/monitor/monitor.go +++ b/core/monitor/monitor.go @@ -39,8 +39,8 @@ type Monitor struct { logVerifier merkle.LogVerifier signer *tcrypto.Signer // TODO(ismail): update last trusted signed log root - //trusted trillian.SignedLogRoot - store *storage.Storage + trusted *trillian.SignedLogRoot + store *storage.Storage } // New creates a new instance of the monitor. diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 1deadfb45..50374af31 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -18,14 +18,181 @@ package monitor import ( + "bytes" + "errors" + "math/big" + + "github.com/golang/glog" + + "github.com/google/trillian/merkle" + "github.com/google/trillian/storage" + + tcrypto "github.com/google/trillian/crypto" + + "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" ) -// verifyMutationsResponse verifies a response received by the GetMutations API. +var ( + // ErrInvalidMutation occurs when verification failed because of an invalid + // mutation. + ErrInvalidMutation = errors.New("invalid mutation") + // ErrNotMatchingMapRoot occurs when the reconstructed root differs from the one + // we received from the server. + ErrNotMatchingMapRoot = errors.New("recreated root does not match") + // ErrInvalidMapSignature occurs if the map roots signature does not verify. + ErrInvalidMapSignature = errors.New("invalid signature on map root") + // ErrInvalidLogSignature occurs if the log roots signature does not verify. + ErrInvalidLogSignature = errors.New("invalid signature on log root") + // ErrInconsistentProofs occurs when the server returned different hashes + // for the same inclusion proof node in the tree. + ErrInconsistentProofs = errors.New("inconsistent inclusion proofs") + // ErrInvalidConsistencyProof occurs when the log consistency proof does not + // verify. + ErrInvalidConsistencyProof = errors.New("invalid log consistency proof") +) + +// verifyResponse verifies a response received by the GetMutations API. // Additionally to the response it takes a complete list of mutations. The list // of received mutations may differ from those included in the initial response -// because of the max. page size. If any verification check failed it returns -// an error. +// because of the max. page size. func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error { + errList := make([]error, 0) + // copy of singed map root + smr := *in.GetSmr() + // reset to the state before it was signed: + smr.Signature = nil + // verify signature on map root: + if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { + glog.Infof("couldn't verify signature on map root: %v", err) + errList = append(errList, ErrInvalidMapSignature) + } + + logRoot := in.GetLogRoot() + // Verify SignedLogRoot signature. + hash := tcrypto.HashLogRoot(*logRoot) + if err := tcrypto.Verify(m.logPubKey, hash, logRoot.GetSignature()); err != nil { + glog.Infof("couldn't verify signature on log root: %v", err) + errList = append(errList, ErrInvalidLogSignature) + } + + if m.trusted != nil { + // Verify consistency proof: + err := m.logVerifier.VerifyConsistencyProof( + m.trusted.TreeSize, logRoot.TreeSize, + m.trusted.RootHash, logRoot.RootHash, + in.GetLogConsistency()) + if err != nil { + errList = append(errList, ErrInvalidConsistencyProof) + } + } else { + // trust the first log root we see, don't verify anything yet + m.trusted = in.GetLogRoot() + } + + // m.logVerifier.VerifyInclusionProof() + // + // retrieve the old root hash from storage! + monRes, err := m.store.Get(in.Epoch - 1) + if err != nil { + glog.Infof("Could not retrieve previous monitoring result: %v", err) + } + // we need the old root for verifying the inclusion of the old leafs in the + // previous epoch. Storage always stores the mutations response independent + // from if the checks succeeded or not. + oldRoot := monRes.Response.GetSmr().GetRootHash() + + if err := m.verifyMutations(in.GetMutations(), oldRoot, + in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { + errList = append(errList, err...) + } + + return errList +} + +func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoot []byte, mapID int64) []error { + errList := make([]error, 0) + mutator := entry.New() + oldProofNodes := make(map[string][]byte) + newLeaves := make([]merkle.HStar2LeafHash, 0, len(muts)) + + for _, mut := range muts { + oldLeaf, err := entry.FromLeafValue(mut.GetProof().GetLeaf().GetLeafValue()) + if err != nil { + errList = append(errList, ErrInvalidMutation) + } + + // verify that the provided leaf’s inclusion proof goes to epoch e-1: + index := mut.GetProof().GetLeaf().GetIndex() + leafHash := mut.GetProof().GetLeaf().GetLeafHash() + if err := merkle.VerifyMapInclusionProof(mapID, index, + leafHash, oldRoot, mut.GetProof().GetInclusion(), m.hasher); err != nil { + glog.Infof("VerifyMapInclusionProof(%x): %v", index, err) + errList = append(errList, err) + } + + // compute the new leaf + newLeaf, err := mutator.Mutate(oldLeaf, mut.GetUpdate()) + if err != nil { + errList = append(errList, ErrInvalidMutation) + } + newLeafnID := storage.NewNodeIDFromPrefixSuffix(index, storage.Suffix{}, m.hasher.BitLen()) + newLeafHash := m.hasher.HashLeaf(mapID, index, newLeaf) + newLeaves = append(newLeaves, merkle.HStar2LeafHash{ + Index: newLeafnID.BigInt(), + LeafHash: newLeafHash, + }) + + // store the proof hashes locally to recompute the tree below: + sibIDs := newLeafnID.Siblings() + // TODO(ismail) iterate over the sibIDs instead! + for level, proof := range mut.GetProof().GetInclusion() { + pID := sibIDs[level] + if p, ok := oldProofNodes[pID.String()]; ok { + // sanity check: for each mut overlapping proof nodes should be + // equal: + if !bytes.Equal(p, proof) { + // TODO(ismail): remove this check and this error type as + // soon as the server does not return interior proof nodes + // multiple times + // + // this is really odd and should never happen + errList = append(errList, ErrInconsistentProofs) + } + } else { + oldProofNodes[pID.String()] = proof + } + } + } + if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil { + errList = append(errList, err) + } + + return errList +} + +func (m *Monitor) validateMapRoot(expectedRoot []byte, mapID int64, mutatedLeaves []merkle.HStar2LeafHash, oldProofNodes map[string][]byte) error { + // compute the new root using local intermediate hashes from epoch e + // (above proof hashes): + hs2 := merkle.NewHStar2(mapID, m.hasher) + newRoot, err := hs2.HStar2Nodes([]byte{}, m.hasher.Size(), mutatedLeaves, + func(depth int, index *big.Int) ([]byte, error) { + nID := storage.NewNodeIDFromBigInt(depth, index, m.hasher.BitLen()) + if p, ok := oldProofNodes[nID.String()]; ok { + return p, nil + } + return nil, nil + }, nil) + + if err != nil { + glog.Errorf("hs2.HStar2Nodes(_): %v", err) + // TODO(ismail): figure out what to return here + } + + // verify rootHash + if !bytes.Equal(newRoot, expectedRoot) { + return ErrNotMatchingMapRoot + } + return nil } From e306d7d97a716cd9d45596645d1eceea90b71bbe Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 24 Aug 2017 12:26:11 -0400 Subject: [PATCH 02/10] more verification steps --- core/monitor/monitor.go | 11 +++-- core/monitor/verify.go | 102 ++++++++++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go index 72c941bcc..640f03633 100644 --- a/core/monitor/monitor.go +++ b/core/monitor/monitor.go @@ -33,14 +33,14 @@ import ( // Monitor holds the internal state for a monitor accessing the mutations API // and for verifying its responses. type Monitor struct { - hasher hashers.MapHasher + logHasher hashers.LogHasher + mapHasher hashers.MapHasher logPubKey crypto.PublicKey mapPubKey crypto.PublicKey logVerifier merkle.LogVerifier signer *tcrypto.Signer - // TODO(ismail): update last trusted signed log root - trusted *trillian.SignedLogRoot - store *storage.Storage + trusted *trillian.SignedLogRoot + store *storage.Storage } // New creates a new instance of the monitor. @@ -54,7 +54,8 @@ func New(logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage return nil, fmt.Errorf("Failed creating MapHasher: %v", err) } return &Monitor{ - hasher: mapHasher, + mapHasher: mapHasher, + logHasher: logHasher, logVerifier: merkle.NewLogVerifier(logHasher), logPubKey: logTree.GetPublicKey(), mapPubKey: mapTree.GetPublicKey(), diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 50374af31..07811fedc 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -19,6 +19,7 @@ package monitor import ( "bytes" + "encoding/json" "errors" "math/big" @@ -37,8 +38,8 @@ var ( // ErrInvalidMutation occurs when verification failed because of an invalid // mutation. ErrInvalidMutation = errors.New("invalid mutation") - // ErrNotMatchingMapRoot occurs when the reconstructed root differs from the one - // we received from the server. + // ErrNotMatchingMapRoot occurs when the reconstructed root differs from the + // one we received from the server. ErrNotMatchingMapRoot = errors.New("recreated root does not match") // ErrInvalidMapSignature occurs if the map roots signature does not verify. ErrInvalidMapSignature = errors.New("invalid signature on map root") @@ -47,9 +48,12 @@ var ( // ErrInconsistentProofs occurs when the server returned different hashes // for the same inclusion proof node in the tree. ErrInconsistentProofs = errors.New("inconsistent inclusion proofs") - // ErrInvalidConsistencyProof occurs when the log consistency proof does not - // verify. - ErrInvalidConsistencyProof = errors.New("invalid log consistency proof") + // ErrInvalidLogConsistencyProof occurs when the log consistency proof does + // not verify. + ErrInvalidLogConsistencyProof = errors.New("invalid log consistency proof") + // ErrInvalidLogInclusion occurs if the inclusion proof for the signed map + // root into the log does not verify. + ErrInvalidLogInclusion = errors.New("invalid log inclusion proof") ) // verifyResponse verifies a response received by the GetMutations API. @@ -58,15 +62,10 @@ var ( // because of the max. page size. func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error { errList := make([]error, 0) - // copy of singed map root - smr := *in.GetSmr() - // reset to the state before it was signed: - smr.Signature = nil - // verify signature on map root: - if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { - glog.Infof("couldn't verify signature on map root: %v", err) - errList = append(errList, ErrInvalidMapSignature) - } + + // + // log verification + // logRoot := in.GetLogRoot() // Verify SignedLogRoot signature. @@ -76,32 +75,67 @@ func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, ErrInvalidLogSignature) } - if m.trusted != nil { + if m.trusted != nil && m.trusted.GetTreeSize() > 0 { // Verify consistency proof: err := m.logVerifier.VerifyConsistencyProof( m.trusted.TreeSize, logRoot.TreeSize, m.trusted.RootHash, logRoot.RootHash, in.GetLogConsistency()) if err != nil { - errList = append(errList, ErrInvalidConsistencyProof) + errList = append(errList, ErrInvalidLogConsistencyProof) } } else { // trust the first log root we see, don't verify anything yet m.trusted = in.GetLogRoot() } - // m.logVerifier.VerifyInclusionProof() + b, err := json.Marshal(in.GetSmr()) + if err != nil { + glog.Errorf("json.Marshal(): %v", err) + // Encoding error + } + leafIndex := in.GetSmr().GetMapRevision() + treeSize := in.GetLogRoot().GetTreeSize() + leafHash := m.logHasher.HashLeaf(b) + err = m.logVerifier.VerifyInclusionProof( + leafIndex, + treeSize, + in.GetLogInclusion(), + in.GetLogRoot().GetRootHash(), + leafHash) + if err != nil { + glog.Errorf("m.logVerifier.VerifyInclusionProof((%v, %v, _): %v", leafIndex, treeSize, err) + errList = append(errList, ErrInvalidLogInclusion) + } + // + // map verification + // + + // copy of singed map root + smr := *in.GetSmr() + // reset to the state before it was signed: + smr.Signature = nil + // verify signature on map root: + if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { + glog.Infof("couldn't verify signature on map root: %v", err) + errList = append(errList, ErrInvalidMapSignature) + } + // retrieve the old root hash from storage! monRes, err := m.store.Get(in.Epoch - 1) if err != nil { glog.Infof("Could not retrieve previous monitoring result: %v", err) } + + // + // mutations verification: + // + // we need the old root for verifying the inclusion of the old leafs in the // previous epoch. Storage always stores the mutations response independent // from if the checks succeeded or not. oldRoot := monRes.Response.GetSmr().GetRootHash() - if err := m.verifyMutations(in.GetMutations(), oldRoot, in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { errList = append(errList, err...) @@ -126,9 +160,9 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo index := mut.GetProof().GetLeaf().GetIndex() leafHash := mut.GetProof().GetLeaf().GetLeafHash() if err := merkle.VerifyMapInclusionProof(mapID, index, - leafHash, oldRoot, mut.GetProof().GetInclusion(), m.hasher); err != nil { + leafHash, oldRoot, mut.GetProof().GetInclusion(), m.mapHasher); err != nil { glog.Infof("VerifyMapInclusionProof(%x): %v", index, err) - errList = append(errList, err) + errList = append(errList, ErrInvalidMutation) } // compute the new leaf @@ -136,8 +170,8 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo if err != nil { errList = append(errList, ErrInvalidMutation) } - newLeafnID := storage.NewNodeIDFromPrefixSuffix(index, storage.Suffix{}, m.hasher.BitLen()) - newLeafHash := m.hasher.HashLeaf(mapID, index, newLeaf) + newLeafnID := storage.NewNodeIDFromPrefixSuffix(index, storage.Suffix{}, m.mapHasher.BitLen()) + newLeafHash := m.mapHasher.HashLeaf(mapID, index, newLeaf) newLeaves = append(newLeaves, merkle.HStar2LeafHash{ Index: newLeafnID.BigInt(), LeafHash: newLeafHash, @@ -145,25 +179,23 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo // store the proof hashes locally to recompute the tree below: sibIDs := newLeafnID.Siblings() - // TODO(ismail) iterate over the sibIDs instead! - for level, proof := range mut.GetProof().GetInclusion() { - pID := sibIDs[level] - if p, ok := oldProofNodes[pID.String()]; ok { + proofs := mut.GetProof().GetInclusion() + for level, sibID := range sibIDs { + proof := proofs[level] + if p, ok := oldProofNodes[sibID.String()]; ok { // sanity check: for each mut overlapping proof nodes should be // equal: if !bytes.Equal(p, proof) { - // TODO(ismail): remove this check and this error type as - // soon as the server does not return interior proof nodes - // multiple times - // // this is really odd and should never happen errList = append(errList, ErrInconsistentProofs) } } else { - oldProofNodes[pID.String()] = proof + oldProofNodes[sibID.String()] = proof } + } } + if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil { errList = append(errList, err) } @@ -174,10 +206,10 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo func (m *Monitor) validateMapRoot(expectedRoot []byte, mapID int64, mutatedLeaves []merkle.HStar2LeafHash, oldProofNodes map[string][]byte) error { // compute the new root using local intermediate hashes from epoch e // (above proof hashes): - hs2 := merkle.NewHStar2(mapID, m.hasher) - newRoot, err := hs2.HStar2Nodes([]byte{}, m.hasher.Size(), mutatedLeaves, + hs2 := merkle.NewHStar2(mapID, m.mapHasher) + newRoot, err := hs2.HStar2Nodes([]byte{}, m.mapHasher.Size(), mutatedLeaves, func(depth int, index *big.Int) ([]byte, error) { - nID := storage.NewNodeIDFromBigInt(depth, index, m.hasher.BitLen()) + nID := storage.NewNodeIDFromBigInt(depth, index, m.mapHasher.BitLen()) if p, ok := oldProofNodes[nID.String()]; ok { return p, nil } @@ -186,7 +218,7 @@ func (m *Monitor) validateMapRoot(expectedRoot []byte, mapID int64, mutatedLeave if err != nil { glog.Errorf("hs2.HStar2Nodes(_): %v", err) - // TODO(ismail): figure out what to return here + return ErrNotMatchingMapRoot } // verify rootHash From 4680c1e87a5605566858bd5bfcf613829130a3fb Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 24 Aug 2017 17:13:17 -0400 Subject: [PATCH 03/10] first crack on integration tests --- core/client/kt/verify.go | 4 ++ core/monitor/monitor.go | 44 ++++++++++------- core/monitor/verify.go | 47 ++++++++++++------ impl/monitor/client/client.go | 8 ++-- integration/monitor_test.go | 89 +++++++++++++++++++++++++++++++++++ integration/testutil.go | 28 +++++++---- 6 files changed, 178 insertions(+), 42 deletions(-) create mode 100644 integration/monitor_test.go diff --git a/core/client/kt/verify.go b/core/client/kt/verify.go index d11d0e290..248691cc7 100644 --- a/core/client/kt/verify.go +++ b/core/client/kt/verify.go @@ -120,6 +120,10 @@ func (v *Verifier) VerifyGetEntryResponse(ctx context.Context, userID, appID str // by removing the signature from the object. smr := *in.GetSmr() smr.Signature = nil // Remove the signature from the object to be verified. + fmt.Println("CLIENT tcrypto.VerifyObject:") + fmt.Println(v.mapPubKey) + fmt.Println(smr) + fmt.Println(in.GetSmr().GetSignature()) if err := tcrypto.VerifyObject(v.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { Vlog.Printf("✗ Signed Map Head signature verification failed.") return fmt.Errorf("sig.Verify(SMR): %v", err) diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go index 640f03633..649fb8a7c 100644 --- a/core/monitor/monitor.go +++ b/core/monitor/monitor.go @@ -25,26 +25,30 @@ import ( ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" "github.com/google/trillian" + "github.com/google/trillian/client" tcrypto "github.com/google/trillian/crypto" "github.com/google/trillian/merkle" "github.com/google/trillian/merkle/hashers" + "github.com/google/trillian/crypto/keys/der" ) // Monitor holds the internal state for a monitor accessing the mutations API // and for verifying its responses. type Monitor struct { - logHasher hashers.LogHasher - mapHasher hashers.MapHasher - logPubKey crypto.PublicKey - mapPubKey crypto.PublicKey - logVerifier merkle.LogVerifier - signer *tcrypto.Signer - trusted *trillian.SignedLogRoot - store *storage.Storage + mapID int64 + logHasher hashers.LogHasher + mapHasher hashers.MapHasher + logPubKey crypto.PublicKey + mapPubKey crypto.PublicKey + logVerifier merkle.LogVerifier + logVerifierCli client.LogVerifier + signer *tcrypto.Signer + trusted *trillian.SignedLogRoot + store *storage.Storage } // New creates a new instance of the monitor. -func New(logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage.Storage) (*Monitor, error) { +func New(logverifierCli client.LogVerifier, logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage.Storage) (*Monitor, error) { logHasher, err := hashers.NewLogHasher(logTree.GetHashStrategy()) if err != nil { return nil, fmt.Errorf("Failed creating LogHasher: %v", err) @@ -53,14 +57,20 @@ func New(logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage if err != nil { return nil, fmt.Errorf("Failed creating MapHasher: %v", err) } + mapPubKey, err := der.UnmarshalPublicKey(mapTree.GetPublicKey().GetDer()) + if err != nil { + return nil, fmt.Errorf("Could not unmarshal map public key: %v", err) + } return &Monitor{ - mapHasher: mapHasher, - logHasher: logHasher, - logVerifier: merkle.NewLogVerifier(logHasher), - logPubKey: logTree.GetPublicKey(), - mapPubKey: mapTree.GetPublicKey(), - signer: signer, - store: store, + logVerifierCli: logverifierCli, + mapID: mapTree.TreeId, + mapHasher: mapHasher, + logHasher: logHasher, + logVerifier: merkle.NewLogVerifier(logHasher), + logPubKey: logTree.GetPublicKey(), + mapPubKey: mapPubKey, + signer: signer, + store: store, }, nil } @@ -71,7 +81,7 @@ func (m *Monitor) Process(resp *ktpb.GetMutationsResponse) error { var smr *trillian.SignedMapRoot var err error seen := time.Now().Unix() - errs := m.verifyMutationsResponse(resp) + errs := m.VerifyMutationsResponse(resp) if len(errs) == 0 { glog.Infof("Successfully verified mutations response for epoch: %v", resp.Epoch) smr, err = m.signMapRoot(resp) diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 07811fedc..77a0a8123 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -32,6 +32,7 @@ import ( "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" + "fmt" ) var ( @@ -56,22 +57,33 @@ var ( ErrInvalidLogInclusion = errors.New("invalid log inclusion proof") ) -// verifyResponse verifies a response received by the GetMutations API. +// VerifyMutationsResponse verifies a response received by the GetMutations API. // Additionally to the response it takes a complete list of mutations. The list // of received mutations may differ from those included in the initial response // because of the max. page size. -func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error { +func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error { errList := make([]error, 0) // // log verification // + if m.trusted == nil { + m.trusted = in.GetLogRoot() + } + + // TODO(ismail): pass in a (trillian) logverifier instead + // - create an equivalent map verifier (in trillian) + // between different error types (like below) + // - create a set of fixed error messages so the caller can differentiate + if err := m.logVerifierCli.VerifyRoot(m.trusted, in.GetLogRoot(), in.GetLogInclusion()); err != nil { + errList = append(errList, err) + } logRoot := in.GetLogRoot() // Verify SignedLogRoot signature. hash := tcrypto.HashLogRoot(*logRoot) if err := tcrypto.Verify(m.logPubKey, hash, logRoot.GetSignature()); err != nil { - glog.Infof("couldn't verify signature on log root: %v", err) + glog.Infof("couldn't verify signature on log root: %v: %v", logRoot, err) errList = append(errList, ErrInvalidLogSignature) } @@ -117,17 +129,15 @@ func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // reset to the state before it was signed: smr.Signature = nil // verify signature on map root: + fmt.Println("tcrypto.VerifyObject:") + fmt.Println(m.mapPubKey) + fmt.Println(smr) + fmt.Println(in.GetSmr().GetSignature()) if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { glog.Infof("couldn't verify signature on map root: %v", err) errList = append(errList, ErrInvalidMapSignature) } - // retrieve the old root hash from storage! - monRes, err := m.store.Get(in.Epoch - 1) - if err != nil { - glog.Infof("Could not retrieve previous monitoring result: %v", err) - } - // // mutations verification: // @@ -135,10 +145,20 @@ func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // we need the old root for verifying the inclusion of the old leafs in the // previous epoch. Storage always stores the mutations response independent // from if the checks succeeded or not. - oldRoot := monRes.Response.GetSmr().GetRootHash() - if err := m.verifyMutations(in.GetMutations(), oldRoot, - in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { - errList = append(errList, err...) + var oldRoot []byte + if m.store.LatestEpoch() > 0 { + // retrieve the old root hash from storage! + monRes, err := m.store.Get(in.Epoch - 1) + if err != nil { + glog.Infof("Could not retrieve previous monitoring result: %v", err) + } + oldRoot = monRes.Response.GetSmr().GetRootHash() + if err := m.verifyMutations(in.GetMutations(), oldRoot, + in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { + errList = append(errList, err...) + } + } else { + // TODO oldRoot is the hash of the initial tree } return errList @@ -192,7 +212,6 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo } else { oldProofNodes[sibID.String()] = proof } - } } diff --git a/impl/monitor/client/client.go b/impl/monitor/client/client.go index 18245eb76..13e15be5e 100644 --- a/impl/monitor/client/client.go +++ b/impl/monitor/client/client.go @@ -64,9 +64,9 @@ func (c *Client) StartPolling(startEpoch int64) (<-chan *ktpb.GetMutationsRespon glog.Infof("Polling: %v", now) // time out if we exceed the poll period: ctx, _ := context.WithTimeout(context.Background(), c.pollPeriod) - monitorResp, err := c.pollMutations(ctx, epoch) + monitorResp, err := c.PollMutations(ctx, epoch) if err != nil { - glog.Infof("pollMutations(_): %v", err) + glog.Infof("PollMutations(_): %v", err) errChan <- err } else { // only write to results channel and increment epoch @@ -80,7 +80,9 @@ func (c *Client) StartPolling(startEpoch int64) (<-chan *ktpb.GetMutationsRespon return response, errChan } -func (c *Client) pollMutations(ctx context.Context, +// PollMutations polls GetMutationsResponses from the configured +// key-transparency server's mutations API. +func (c *Client) PollMutations(ctx context.Context, queryEpoch int64, opts ...grpc.CallOption) (*ktpb.GetMutationsResponse, error) { response, err := c.client.GetMutations(ctx, &ktpb.GetMutationsRequest{ diff --git a/integration/monitor_test.go b/integration/monitor_test.go new file mode 100644 index 000000000..fc4465951 --- /dev/null +++ b/integration/monitor_test.go @@ -0,0 +1,89 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "context" + "testing" + "time" + + "github.com/google/keytransparency/core/monitor" + "github.com/google/keytransparency/core/monitor/storage" + "github.com/google/keytransparency/impl/monitor/client" + kpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" + spb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service" + mupb "github.com/google/keytransparency/impl/proto/mutation_v1_service" + "github.com/google/trillian/crypto" + "github.com/google/trillian/crypto/keys/pem" + + "github.com/google/keytransparency/core/fake" +) + +const ( + monitorPrivKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAV7H3qRi/cj/w04vEQBFjLdhcXRbZR4ouT5zaAy1XUHoAoGCCqGSM49 +AwEHoUQDQgAEqUDbATN2maGIm6YQLpjx67bYN1hxPPdF0VrPTZe36yQhH+GCwZQV +amFdON6OhjYnBmJWe4fVnbxny0PfpkvXtg== +-----END EC PRIVATE KEY-----` +) + +func TestMonitorEmptyStart(t *testing.T) { + bctx := context.Background() + env := NewEnv(t) + defer env.Close(t) + env.Client.RetryCount = 0 + + // setup monitor: + + // TODO(ismail) setup a proper log environment in the integration + // environment, then use GetDomainInfo here: + c := spb.NewKeyTransparencyServiceClient(env.Conn) + resp, err := c.GetDomainInfo(bctx, &kpb.GetDomainInfoRequest{}) + if err != nil { + t.Fatalf("Couldn't retrieve domain info: %v", err) + } + signer, err := pem.UnmarshalPrivateKey(monitorPrivKey, "") + if err != nil { + t.Fatalf("Couldn't create signer: %v", err) + } + logTree := resp.Log + mapTree := resp.Map + store := storage.New() + mon, err := monitor.New(fake.NewFakeTrillianLogVerifier(), logTree, mapTree, crypto.NewSHA256Signer(signer), store) + if err != nil { + t.Fatalf("Couldn't create monitor: %v", err) + } + // Initialization and CreateEpoch is called by NewEnv + mcc := mupb.NewMutationServiceClient(env.Conn) + mutCli := client.New(mcc, time.Second) + // verify first SMR + mutResp, err := mutCli.PollMutations(bctx, 1) + if err != nil { + t.Fatalf("Could not query mutations: %v", err) + } + _ = mon + if err := mon.Process(mutResp); err != nil { + t.Fatalf("Monitor could process mutations: %v", err) + } + mresp, err := store.Get(1) + if err != nil { + t.Fatalf("Could not read monitoring response: %v", err) + } + for _, err := range mresp.Errors { + t.Errorf("Got error: %v", err) + } + + // TODO client sends one mutation, sequencer "signs", monitor verifies +} diff --git a/integration/testutil.go b/integration/testutil.go index 5a0198f3e..52c7f1fb5 100644 --- a/integration/testutil.go +++ b/integration/testutil.go @@ -44,11 +44,10 @@ import ( _ "github.com/mattn/go-sqlite3" // Use sqlite database for testing. pb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service" + mpb "github.com/google/keytransparency/impl/proto/mutation_v1_service" + cmutation "github.com/google/keytransparency/core/mutation" stestonly "github.com/google/trillian/storage/testonly" -) - -const ( - logID = 0 + "github.com/google/keytransparency/impl/mutation" ) // NewDB creates a new in-memory database for testing. @@ -130,12 +129,25 @@ func NewEnv(t *testing.T) *Env { t.Fatalf("CreateTree(): %v", err) } mapID := tree.TreeId - mapPubKey, err := der.UnmarshalPublicKey(tree.GetPublicKey().GetDer()) if err != nil { t.Fatalf("Failed to load signing keypair: %v", err) } + // Configure log. + logTree, err := mapEnv.AdminClient.CreateTree(ctx, &trillian.CreateTreeRequest{ + Tree: stestonly.LogTree, + }) + //logPubKey, err := der.UnmarshalPublicKey(tree.GetPublicKey().GetDer()) + //if err != nil { + // t.Fatalf("Failed to load signing keypair: %v", err) + //} + + if err != nil { + t.Fatalf("CreateTree(): %v", err) + } + logID := logTree.GetTreeId() + // Common data structures. mutations, err := mutations.New(sqldb, mapID) if err != nil { @@ -152,15 +164,15 @@ func NewEnv(t *testing.T) *Env { t.Fatalf("Failed to create committer: %v", err) } authz := authorization.New() - tlog := fake.NewFakeTrillianLogClient() - tadmin := trillian.NewTrillianAdminClient(nil) factory := transaction.NewFactory(sqldb) - server := keyserver.New(logID, tlog, mapID, mapEnv.MapClient, tadmin, commitments, + server := keyserver.New(logID, tlog, mapID, mapEnv.MapClient, mapEnv.AdminClient, commitments, vrfPriv, mutator, auth, authz, factory, mutations) s := grpc.NewServer() + msrv := mutation.New(cmutation.New(logID, mapID, tlog, mapEnv.MapClient, mutations, factory)) pb.RegisterKeyTransparencyServiceServer(s, server) + mpb.RegisterMutationServiceServer(s, msrv) // Signer signer := sequencer.New(mapID, mapEnv.MapClient, logID, tlog, mutator, mutations, factory) From 588c0c0f44dd9dfbc4d9f6c3dc195923cbbb5df6 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 24 Aug 2017 21:35:56 -0400 Subject: [PATCH 04/10] use client.LogVerifier instead merkler.LogVerifier --- core/monitor/monitor.go | 40 +++++++++++++------------------- core/monitor/verify.go | 51 +++++++++-------------------------------- 2 files changed, 27 insertions(+), 64 deletions(-) diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go index 649fb8a7c..8b4ff26a0 100644 --- a/core/monitor/monitor.go +++ b/core/monitor/monitor.go @@ -35,24 +35,19 @@ import ( // Monitor holds the internal state for a monitor accessing the mutations API // and for verifying its responses. type Monitor struct { - mapID int64 - logHasher hashers.LogHasher - mapHasher hashers.MapHasher - logPubKey crypto.PublicKey - mapPubKey crypto.PublicKey - logVerifier merkle.LogVerifier - logVerifierCli client.LogVerifier - signer *tcrypto.Signer - trusted *trillian.SignedLogRoot - store *storage.Storage + mapID int64 + logHasher hashers.LogHasher + mapHasher hashers.MapHasher + logPubKey crypto.PublicKey + mapPubKey crypto.PublicKey + logVerifier client.LogVerifier + signer *tcrypto.Signer + trusted *trillian.SignedLogRoot + store *storage.Storage } // New creates a new instance of the monitor. -func New(logverifierCli client.LogVerifier, logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage.Storage) (*Monitor, error) { - logHasher, err := hashers.NewLogHasher(logTree.GetHashStrategy()) - if err != nil { - return nil, fmt.Errorf("Failed creating LogHasher: %v", err) - } +func New(logverifierCli client.LogVerifier, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage.Storage) (*Monitor, error) { mapHasher, err := hashers.NewMapHasher(mapTree.GetHashStrategy()) if err != nil { return nil, fmt.Errorf("Failed creating MapHasher: %v", err) @@ -62,15 +57,12 @@ func New(logverifierCli client.LogVerifier, logTree, mapTree *trillian.Tree, sig return nil, fmt.Errorf("Could not unmarshal map public key: %v", err) } return &Monitor{ - logVerifierCli: logverifierCli, - mapID: mapTree.TreeId, - mapHasher: mapHasher, - logHasher: logHasher, - logVerifier: merkle.NewLogVerifier(logHasher), - logPubKey: logTree.GetPublicKey(), - mapPubKey: mapPubKey, - signer: signer, - store: store, + logVerifier: logverifierCli, + mapID: mapTree.TreeId, + mapHasher: mapHasher, + mapPubKey: mapPubKey, + signer: signer, + store: store, }, nil } diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 77a0a8123..1bcb10208 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -32,7 +32,6 @@ import ( "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" - "fmt" ) var ( @@ -73,33 +72,15 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // TODO(ismail): pass in a (trillian) logverifier instead - // - create an equivalent map verifier (in trillian) - // between different error types (like below) // - create a set of fixed error messages so the caller can differentiate - if err := m.logVerifierCli.VerifyRoot(m.trusted, in.GetLogRoot(), in.GetLogInclusion()); err != nil { + // between different error types (like below) + // - also, create an equivalent map verifier (in trillian) + if err := m.logVerifier.VerifyRoot(m.trusted, in.GetLogRoot(), in.GetLogConsistency()); err != nil { + // this could be one of ErrInvalidLogSignature, ErrInvalidLogConsistencyProof errList = append(errList, err) } - logRoot := in.GetLogRoot() - // Verify SignedLogRoot signature. - hash := tcrypto.HashLogRoot(*logRoot) - if err := tcrypto.Verify(m.logPubKey, hash, logRoot.GetSignature()); err != nil { - glog.Infof("couldn't verify signature on log root: %v: %v", logRoot, err) - errList = append(errList, ErrInvalidLogSignature) - } - - if m.trusted != nil && m.trusted.GetTreeSize() > 0 { - // Verify consistency proof: - err := m.logVerifier.VerifyConsistencyProof( - m.trusted.TreeSize, logRoot.TreeSize, - m.trusted.RootHash, logRoot.RootHash, - in.GetLogConsistency()) - if err != nil { - errList = append(errList, ErrInvalidLogConsistencyProof) - } - } else { - // trust the first log root we see, don't verify anything yet - m.trusted = in.GetLogRoot() - } + // updated trusted log root + m.trusted = in.GetLogRoot() b, err := json.Marshal(in.GetSmr()) if err != nil { @@ -108,15 +89,9 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error } leafIndex := in.GetSmr().GetMapRevision() treeSize := in.GetLogRoot().GetTreeSize() - leafHash := m.logHasher.HashLeaf(b) - err = m.logVerifier.VerifyInclusionProof( - leafIndex, - treeSize, - in.GetLogInclusion(), - in.GetLogRoot().GetRootHash(), - leafHash) + err = m.logVerifier.VerifyInclusionAtIndex(in.GetLogRoot(), b, leafIndex, in.GetLogInclusion()) if err != nil { - glog.Errorf("m.logVerifier.VerifyInclusionProof((%v, %v, _): %v", leafIndex, treeSize, err) + glog.Errorf("m.logVerifier.VerifyInclusionAtIndex((%v, %v, _): %v", leafIndex, treeSize, err) errList = append(errList, ErrInvalidLogInclusion) } @@ -129,24 +104,20 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // reset to the state before it was signed: smr.Signature = nil // verify signature on map root: - fmt.Println("tcrypto.VerifyObject:") - fmt.Println(m.mapPubKey) - fmt.Println(smr) - fmt.Println(in.GetSmr().GetSignature()) if err := tcrypto.VerifyObject(m.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { glog.Infof("couldn't verify signature on map root: %v", err) errList = append(errList, ErrInvalidMapSignature) } // - // mutations verification: + // mutations verification // // we need the old root for verifying the inclusion of the old leafs in the // previous epoch. Storage always stores the mutations response independent // from if the checks succeeded or not. var oldRoot []byte - if m.store.LatestEpoch() > 0 { + if m.store.LatestEpoch() > 1 { // retrieve the old root hash from storage! monRes, err := m.store.Get(in.Epoch - 1) if err != nil { @@ -158,7 +129,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, err...) } } else { - // TODO oldRoot is the hash of the initial tree + // TODO oldRoot is the hash of the initial empty sparse merkle tree } return errList From a7f163dc8ba267abfe383122b805c63cad8aa648 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 24 Aug 2017 21:58:24 -0400 Subject: [PATCH 05/10] Use trillian logverifier instead --- cmd/keytransparency-monitor/main.go | 20 ++++++++++++++++---- core/client/kt/verify.go | 4 ---- core/monitor/monitor.go | 7 ++----- core/monitor/verify.go | 15 ++------------- integration/monitor_test.go | 8 ++++---- integration/testutil.go | 4 ++-- 6 files changed, 26 insertions(+), 32 deletions(-) diff --git a/cmd/keytransparency-monitor/main.go b/cmd/keytransparency-monitor/main.go index b76685507..c422008c4 100644 --- a/cmd/keytransparency-monitor/main.go +++ b/cmd/keytransparency-monitor/main.go @@ -42,7 +42,10 @@ import ( spb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service" mopb "github.com/google/keytransparency/impl/proto/monitor_v1_service" mupb "github.com/google/keytransparency/impl/proto/mutation_v1_service" - _ "github.com/google/trillian/merkle/coniks" // Register coniks + tlogcli "github.com/google/trillian/client" + "github.com/google/trillian/crypto/keys/der" + _ "github.com/google/trillian/merkle/coniks" // Register coniks + "github.com/google/trillian/merkle/hashers" _ "github.com/google/trillian/merkle/objhasher" // Register objhasher ) @@ -141,13 +144,22 @@ func main() { // Insert handlers for other http paths here. mux := http.NewServeMux() mux.Handle("/", gwmux) + logHasher, err := hashers.NewLogHasher(logTree.GetHashStrategy()) + if err != nil { + glog.Fatalf("Could not initialize log hasher: %v", err) + } + logPubKey, err := der.UnmarshalPublicKey(logTree.GetPublicKey().GetDer()) + if err != nil { + glog.Fatalf("Failed parsing Log public key: %v", err) + } + logVerifier := tlogcli.NewLogVerifier(logHasher, logPubKey) - // initialize the mutations API client and feed the responses it got - // into the monitor: - mon, err := cmon.New(logTree, mapTree, crypto.NewSHA256Signer(key), store) + mon, err := cmon.New(logVerifier, mapTree, crypto.NewSHA256Signer(key), store) if err != nil { glog.Exitf("Failed to initialize monitor: %v", err) } + // initialize the mutations API client and feed the responses it got + // into the monitor: mutCli := client.New(mcc, *pollPeriod) responses, errs := mutCli.StartPolling(1) go func() { diff --git a/core/client/kt/verify.go b/core/client/kt/verify.go index 248691cc7..d11d0e290 100644 --- a/core/client/kt/verify.go +++ b/core/client/kt/verify.go @@ -120,10 +120,6 @@ func (v *Verifier) VerifyGetEntryResponse(ctx context.Context, userID, appID str // by removing the signature from the object. smr := *in.GetSmr() smr.Signature = nil // Remove the signature from the object to be verified. - fmt.Println("CLIENT tcrypto.VerifyObject:") - fmt.Println(v.mapPubKey) - fmt.Println(smr) - fmt.Println(in.GetSmr().GetSignature()) if err := tcrypto.VerifyObject(v.mapPubKey, smr, in.GetSmr().GetSignature()); err != nil { Vlog.Printf("✗ Signed Map Head signature verification failed.") return fmt.Errorf("sig.Verify(SMR): %v", err) diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go index 8b4ff26a0..f1981ec3b 100644 --- a/core/monitor/monitor.go +++ b/core/monitor/monitor.go @@ -27,18 +27,15 @@ import ( "github.com/google/trillian" "github.com/google/trillian/client" tcrypto "github.com/google/trillian/crypto" - "github.com/google/trillian/merkle" - "github.com/google/trillian/merkle/hashers" "github.com/google/trillian/crypto/keys/der" + "github.com/google/trillian/merkle/hashers" ) // Monitor holds the internal state for a monitor accessing the mutations API // and for verifying its responses. type Monitor struct { mapID int64 - logHasher hashers.LogHasher mapHasher hashers.MapHasher - logPubKey crypto.PublicKey mapPubKey crypto.PublicKey logVerifier client.LogVerifier signer *tcrypto.Signer @@ -52,7 +49,7 @@ func New(logverifierCli client.LogVerifier, mapTree *trillian.Tree, signer *tcry if err != nil { return nil, fmt.Errorf("Failed creating MapHasher: %v", err) } - mapPubKey, err := der.UnmarshalPublicKey(mapTree.GetPublicKey().GetDer()) + mapPubKey, err := der.UnmarshalPublicKey(mapTree.GetPublicKey().GetDer()) if err != nil { return nil, fmt.Errorf("Could not unmarshal map public key: %v", err) } diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 1bcb10208..02ce17be9 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -70,11 +70,6 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error m.trusted = in.GetLogRoot() } - - // TODO(ismail): pass in a (trillian) logverifier instead - // - create a set of fixed error messages so the caller can differentiate - // between different error types (like below) - // - also, create an equivalent map verifier (in trillian) if err := m.logVerifier.VerifyRoot(m.trusted, in.GetLogRoot(), in.GetLogConsistency()); err != nil { // this could be one of ErrInvalidLogSignature, ErrInvalidLogConsistencyProof errList = append(errList, err) @@ -85,7 +80,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error b, err := json.Marshal(in.GetSmr()) if err != nil { glog.Errorf("json.Marshal(): %v", err) - // Encoding error + errList = append(errList, ErrInvalidMapSignature) } leafIndex := in.GetSmr().GetMapRevision() treeSize := in.GetLogRoot().GetTreeSize() @@ -95,9 +90,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, ErrInvalidLogInclusion) } - // // map verification - // // copy of singed map root smr := *in.GetSmr() @@ -109,9 +102,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, ErrInvalidMapSignature) } - // // mutations verification - // // we need the old root for verifying the inclusion of the old leafs in the // previous epoch. Storage always stores the mutations response independent @@ -128,9 +119,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { errList = append(errList, err...) } - } else { - // TODO oldRoot is the hash of the initial empty sparse merkle tree - } + } // TODO else oldRoot is the hash of the initial empty sparse merkle tree return errList } diff --git a/integration/monitor_test.go b/integration/monitor_test.go index fc4465951..768c67953 100644 --- a/integration/monitor_test.go +++ b/integration/monitor_test.go @@ -21,8 +21,8 @@ import ( "github.com/google/keytransparency/core/monitor" "github.com/google/keytransparency/core/monitor/storage" - "github.com/google/keytransparency/impl/monitor/client" kpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" + "github.com/google/keytransparency/impl/monitor/client" spb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service" mupb "github.com/google/keytransparency/impl/proto/mutation_v1_service" "github.com/google/trillian/crypto" @@ -54,14 +54,14 @@ func TestMonitorEmptyStart(t *testing.T) { if err != nil { t.Fatalf("Couldn't retrieve domain info: %v", err) } - signer, err := pem.UnmarshalPrivateKey(monitorPrivKey, "") + signer, err := pem.UnmarshalPrivateKey(monitorPrivKey, "") if err != nil { t.Fatalf("Couldn't create signer: %v", err) } - logTree := resp.Log + //logTree := resp.Log mapTree := resp.Map store := storage.New() - mon, err := monitor.New(fake.NewFakeTrillianLogVerifier(), logTree, mapTree, crypto.NewSHA256Signer(signer), store) + mon, err := monitor.New(fake.NewFakeTrillianLogVerifier(), mapTree, crypto.NewSHA256Signer(signer), store) if err != nil { t.Fatalf("Couldn't create monitor: %v", err) } diff --git a/integration/testutil.go b/integration/testutil.go index 52c7f1fb5..172b3915f 100644 --- a/integration/testutil.go +++ b/integration/testutil.go @@ -43,11 +43,11 @@ import ( _ "github.com/mattn/go-sqlite3" // Use sqlite database for testing. + cmutation "github.com/google/keytransparency/core/mutation" + "github.com/google/keytransparency/impl/mutation" pb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service" mpb "github.com/google/keytransparency/impl/proto/mutation_v1_service" - cmutation "github.com/google/keytransparency/core/mutation" stestonly "github.com/google/trillian/storage/testonly" - "github.com/google/keytransparency/impl/mutation" ) // NewDB creates a new in-memory database for testing. From 0da8fdac80ded87c03b3d0eeda2247956125f73c Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 24 Aug 2017 23:16:12 -0400 Subject: [PATCH 06/10] WIP: fix mutator bug --- core/monitor/verify.go | 11 +++++++++-- core/mutation/mutation.go | 12 +++++++++--- integration/monitor_test.go | 30 ++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 02ce17be9..253d870c8 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -32,6 +32,7 @@ import ( "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" + "fmt" ) var ( @@ -108,13 +109,15 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // previous epoch. Storage always stores the mutations response independent // from if the checks succeeded or not. var oldRoot []byte - if m.store.LatestEpoch() > 1 { + if m.store.LatestEpoch() > 0 { + fmt.Println("Called") // retrieve the old root hash from storage! monRes, err := m.store.Get(in.Epoch - 1) if err != nil { glog.Infof("Could not retrieve previous monitoring result: %v", err) } oldRoot = monRes.Response.GetSmr().GetRootHash() + if err := m.verifyMutations(in.GetMutations(), oldRoot, in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { errList = append(errList, err...) @@ -129,7 +132,7 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo mutator := entry.New() oldProofNodes := make(map[string][]byte) newLeaves := make([]merkle.HStar2LeafHash, 0, len(muts)) - + glog.Infof("verifyMutations() called with %v mutations.", len(muts)) for _, mut := range muts { oldLeaf, err := entry.FromLeafValue(mut.GetProof().GetLeaf().GetLeafValue()) if err != nil { @@ -146,8 +149,12 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo } // compute the new leaf + fmt.Println("old leaf: ") + fmt.Println(mut.GetProof().GetLeaf().GetLeafValue()) + fmt.Println(oldLeaf) newLeaf, err := mutator.Mutate(oldLeaf, mut.GetUpdate()) if err != nil { + glog.Infof("Mutation did not verify: %v", err) errList = append(errList, ErrInvalidMutation) } newLeafnID := storage.NewNodeIDFromPrefixSuffix(index, storage.Suffix{}, m.mapHasher.BitLen()) diff --git a/core/mutation/mutation.go b/core/mutation/mutation.go index a8fa2e548..3c3f5f5a7 100644 --- a/core/mutation/mutation.go +++ b/core/mutation/mutation.go @@ -106,7 +106,13 @@ func (s *Server) GetMutations(ctx context.Context, in *tpb.GetMutationsRequest) } // Get leaf proofs. // TODO: allow leaf proofs to be optional. - proofs, err := s.inclusionProofs(ctx, indexes, in.Epoch) + var epoch int64 + if in.Epoch > 1 { + epoch = in.Epoch-1 + } else { + epoch = 1 + } + proofs, err := s.inclusionProofs(ctx, indexes, epoch) if err != nil { return nil, err } @@ -114,10 +120,10 @@ func (s *Server) GetMutations(ctx context.Context, in *tpb.GetMutationsRequest) mutations[i].Proof = p } - // MapRevisions start at 1. Log leave's index starts at 0. + // MapRevisions start at 1. Log leave's index starts at 1. // MapRevision should be at least 1 since the Signer is // supposed to create at least one revision on startup. - respEpoch := resp.GetMapRoot().GetMapRevision() - 1 + respEpoch := resp.GetMapRoot().GetMapRevision() // Fetch log proofs. logRoot, logConsistency, logInclusion, err := s.logProofs(ctx, in.GetFirstTreeSize(), respEpoch) if err != nil { diff --git a/integration/monitor_test.go b/integration/monitor_test.go index 768c67953..ed0f48854 100644 --- a/integration/monitor_test.go +++ b/integration/monitor_test.go @@ -29,6 +29,8 @@ import ( "github.com/google/trillian/crypto/keys/pem" "github.com/google/keytransparency/core/fake" + "github.com/google/keytransparency/core/crypto/signatures" + "github.com/google/keytransparency/cmd/keytransparency-client/grpcc" ) const ( @@ -73,7 +75,6 @@ func TestMonitorEmptyStart(t *testing.T) { if err != nil { t.Fatalf("Could not query mutations: %v", err) } - _ = mon if err := mon.Process(mutResp); err != nil { t.Fatalf("Monitor could process mutations: %v", err) } @@ -85,5 +86,30 @@ func TestMonitorEmptyStart(t *testing.T) { t.Errorf("Got error: %v", err) } - // TODO client sends one mutation, sequencer "signs", monitor verifies + // client sends one mutation, sequencer "signs", monitor verifies + userID := "test@test.com" + signers := []signatures.Signer{createSigner(t, testPrivKey1)} + authorizedKeys := []*kpb.PublicKey{getAuthorizedKey(testPubKey1)} + + _, err = env.Client.Update(GetNewOutgoingContextWithFakeAuth("test@test.com"), userID, appID, []byte("testProfile"), signers, authorizedKeys) + if err != grpcc.ErrRetry { + t.Fatalf("Could not send update request: %v", err) + } + if err := env.Signer.CreateEpoch(bctx, false); err != nil { + t.Errorf("CreateEpoch(_): %v", err) + } + mutResp, err = mutCli.PollMutations(bctx, 2) + if err != nil { + t.Fatalf("Could not query mutations: %v", err) + } + if err := mon.Process(mutResp); err != nil { + t.Fatalf("Monitor could not process mutations: %v", err) + } + mresp, err = store.Get(2) + if err != nil { + t.Fatalf("Could not read monitoring response: %v", err) + } + for _, err := range mresp.Errors { + t.Errorf("Got error: %v", err) + } } From f600e5a441038a7f146ef48654c4bf1f08ea3409 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Fri, 25 Aug 2017 00:20:59 -0400 Subject: [PATCH 07/10] All verifications pass --- core/monitor/verify.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 253d870c8..dc39c4342 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -141,9 +141,9 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo // verify that the provided leaf’s inclusion proof goes to epoch e-1: index := mut.GetProof().GetLeaf().GetIndex() - leafHash := mut.GetProof().GetLeaf().GetLeafHash() + leaf := mut.GetProof().GetLeaf().GetLeafValue() if err := merkle.VerifyMapInclusionProof(mapID, index, - leafHash, oldRoot, mut.GetProof().GetInclusion(), m.mapHasher); err != nil { + leaf, oldRoot, mut.GetProof().GetInclusion(), m.mapHasher); err != nil { glog.Infof("VerifyMapInclusionProof(%x): %v", index, err) errList = append(errList, ErrInvalidMutation) } @@ -177,11 +177,15 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo errList = append(errList, ErrInconsistentProofs) } } else { - oldProofNodes[sibID.String()] = proof + if len(proof) > 0 { + oldProofNodes[sibID.String()] = proof + } } } } - + fmt.Println(newLeaves) + fmt.Println(expectedNewRoot) + fmt.Println(oldProofNodes) if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil { errList = append(errList, err) } @@ -193,7 +197,7 @@ func (m *Monitor) validateMapRoot(expectedRoot []byte, mapID int64, mutatedLeave // compute the new root using local intermediate hashes from epoch e // (above proof hashes): hs2 := merkle.NewHStar2(mapID, m.mapHasher) - newRoot, err := hs2.HStar2Nodes([]byte{}, m.mapHasher.Size(), mutatedLeaves, + newRoot, err := hs2.HStar2Nodes([]byte{}, m.mapHasher.BitLen(), mutatedLeaves, func(depth int, index *big.Int) ([]byte, error) { nID := storage.NewNodeIDFromBigInt(depth, index, m.mapHasher.BitLen()) if p, ok := oldProofNodes[nID.String()]; ok { From af56bbdd18f73743babf7f186fa92fbf1458f381 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Fri, 25 Aug 2017 11:28:23 -0400 Subject: [PATCH 08/10] remove debug output, some minor cleanup --- core/monitor/verify.go | 8 -------- integration/monitor_test.go | 6 ++---- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/core/monitor/verify.go b/core/monitor/verify.go index dc39c4342..e477786cd 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -32,7 +32,6 @@ import ( "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" - "fmt" ) var ( @@ -110,7 +109,6 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error // from if the checks succeeded or not. var oldRoot []byte if m.store.LatestEpoch() > 0 { - fmt.Println("Called") // retrieve the old root hash from storage! monRes, err := m.store.Get(in.Epoch - 1) if err != nil { @@ -149,9 +147,6 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo } // compute the new leaf - fmt.Println("old leaf: ") - fmt.Println(mut.GetProof().GetLeaf().GetLeafValue()) - fmt.Println(oldLeaf) newLeaf, err := mutator.Mutate(oldLeaf, mut.GetUpdate()) if err != nil { glog.Infof("Mutation did not verify: %v", err) @@ -183,9 +178,6 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo } } } - fmt.Println(newLeaves) - fmt.Println(expectedNewRoot) - fmt.Println(oldProofNodes) if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil { errList = append(errList, err) } diff --git a/integration/monitor_test.go b/integration/monitor_test.go index ed0f48854..483a80471 100644 --- a/integration/monitor_test.go +++ b/integration/monitor_test.go @@ -41,16 +41,13 @@ amFdON6OhjYnBmJWe4fVnbxny0PfpkvXtg== -----END EC PRIVATE KEY-----` ) -func TestMonitorEmptyStart(t *testing.T) { +func TestMonitor(t *testing.T) { bctx := context.Background() env := NewEnv(t) defer env.Close(t) env.Client.RetryCount = 0 // setup monitor: - - // TODO(ismail) setup a proper log environment in the integration - // environment, then use GetDomainInfo here: c := spb.NewKeyTransparencyServiceClient(env.Conn) resp, err := c.GetDomainInfo(bctx, &kpb.GetDomainInfoRequest{}) if err != nil { @@ -63,6 +60,7 @@ func TestMonitorEmptyStart(t *testing.T) { //logTree := resp.Log mapTree := resp.Map store := storage.New() + // TODO(ismail): setup and use a real logVerifier instead: mon, err := monitor.New(fake.NewFakeTrillianLogVerifier(), mapTree, crypto.NewSHA256Signer(signer), store) if err != nil { t.Fatalf("Couldn't create monitor: %v", err) From 67c526e9bb9f27905514f2d417fd2d17e2be0791 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Wed, 30 Aug 2017 12:24:04 +0100 Subject: [PATCH 09/10] table-like tests --- core/monitor/verify.go | 34 ++++++------- core/mutation/mutation.go | 2 +- integration/monitor_test.go | 95 +++++++++++++++++++------------------ integration/testutil.go | 5 -- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/core/monitor/verify.go b/core/monitor/verify.go index e477786cd..9f13f138c 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -35,16 +35,6 @@ import ( ) var ( - // ErrInvalidMutation occurs when verification failed because of an invalid - // mutation. - ErrInvalidMutation = errors.New("invalid mutation") - // ErrNotMatchingMapRoot occurs when the reconstructed root differs from the - // one we received from the server. - ErrNotMatchingMapRoot = errors.New("recreated root does not match") - // ErrInvalidMapSignature occurs if the map roots signature does not verify. - ErrInvalidMapSignature = errors.New("invalid signature on map root") - // ErrInvalidLogSignature occurs if the log roots signature does not verify. - ErrInvalidLogSignature = errors.New("invalid signature on log root") // ErrInconsistentProofs occurs when the server returned different hashes // for the same inclusion proof node in the tree. ErrInconsistentProofs = errors.New("inconsistent inclusion proofs") @@ -54,6 +44,16 @@ var ( // ErrInvalidLogInclusion occurs if the inclusion proof for the signed map // root into the log does not verify. ErrInvalidLogInclusion = errors.New("invalid log inclusion proof") + // ErrInvalidLogSignature occurs if the log roots signature does not verify. + ErrInvalidLogSignature = errors.New("invalid signature on log root") + // ErrInvalidMapSignature occurs if the map roots signature does not verify. + ErrInvalidMapSignature = errors.New("invalid signature on map root") + // ErrInvalidMutation occurs when verification failed because of an invalid + // mutation. + ErrInvalidMutation = errors.New("invalid mutation") + // ErrNotMatchingMapRoot occurs when the reconstructed root differs from the + // one we received from the server. + ErrNotMatchingMapRoot = errors.New("recreated root does not match") ) // VerifyMutationsResponse verifies a response received by the GetMutations API. @@ -63,9 +63,6 @@ var ( func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error { errList := make([]error, 0) - // - // log verification - // if m.trusted == nil { m.trusted = in.GetLogRoot() } @@ -90,8 +87,6 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, ErrInvalidLogInclusion) } - // map verification - // copy of singed map root smr := *in.GetSmr() // reset to the state before it was signed: @@ -102,14 +97,13 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error errList = append(errList, ErrInvalidMapSignature) } - // mutations verification - // we need the old root for verifying the inclusion of the old leafs in the // previous epoch. Storage always stores the mutations response independent // from if the checks succeeded or not. var oldRoot []byte + // mutations happen after epoch 1 which is stored in storage: if m.store.LatestEpoch() > 0 { - // retrieve the old root hash from storage! + // retrieve the old root hash from storage monRes, err := m.store.Get(in.Epoch - 1) if err != nil { glog.Infof("Could not retrieve previous monitoring result: %v", err) @@ -120,7 +114,7 @@ func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error in.GetSmr().GetRootHash(), in.GetSmr().GetMapId()); len(err) > 0 { errList = append(errList, err...) } - } // TODO else oldRoot is the hash of the initial empty sparse merkle tree + } return errList } @@ -131,6 +125,7 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo oldProofNodes := make(map[string][]byte) newLeaves := make([]merkle.HStar2LeafHash, 0, len(muts)) glog.Infof("verifyMutations() called with %v mutations.", len(muts)) + for _, mut := range muts { oldLeaf, err := entry.FromLeafValue(mut.GetProof().GetLeaf().GetLeafValue()) if err != nil { @@ -178,6 +173,7 @@ func (m *Monitor) verifyMutations(muts []*ktpb.Mutation, oldRoot, expectedNewRoo } } } + if err := m.validateMapRoot(expectedNewRoot, mapID, newLeaves, oldProofNodes); err != nil { errList = append(errList, err) } diff --git a/core/mutation/mutation.go b/core/mutation/mutation.go index 3c3f5f5a7..f190270fb 100644 --- a/core/mutation/mutation.go +++ b/core/mutation/mutation.go @@ -108,7 +108,7 @@ func (s *Server) GetMutations(ctx context.Context, in *tpb.GetMutationsRequest) // TODO: allow leaf proofs to be optional. var epoch int64 if in.Epoch > 1 { - epoch = in.Epoch-1 + epoch = in.Epoch - 1 } else { epoch = 1 } diff --git a/integration/monitor_test.go b/integration/monitor_test.go index 483a80471..2b3ecc5a1 100644 --- a/integration/monitor_test.go +++ b/integration/monitor_test.go @@ -28,9 +28,9 @@ import ( "github.com/google/trillian/crypto" "github.com/google/trillian/crypto/keys/pem" - "github.com/google/keytransparency/core/fake" - "github.com/google/keytransparency/core/crypto/signatures" "github.com/google/keytransparency/cmd/keytransparency-client/grpcc" + "github.com/google/keytransparency/core/crypto/signatures" + "github.com/google/keytransparency/core/fake" ) const ( @@ -46,9 +46,8 @@ func TestMonitor(t *testing.T) { env := NewEnv(t) defer env.Close(t) env.Client.RetryCount = 0 - - // setup monitor: c := spb.NewKeyTransparencyServiceClient(env.Conn) + // setup monitor: resp, err := c.GetDomainInfo(bctx, &kpb.GetDomainInfoRequest{}) if err != nil { t.Fatalf("Couldn't retrieve domain info: %v", err) @@ -57,57 +56,63 @@ func TestMonitor(t *testing.T) { if err != nil { t.Fatalf("Couldn't create signer: %v", err) } - //logTree := resp.Log + logTree := resp.Log mapTree := resp.Map + _ = logTree store := storage.New() // TODO(ismail): setup and use a real logVerifier instead: mon, err := monitor.New(fake.NewFakeTrillianLogVerifier(), mapTree, crypto.NewSHA256Signer(signer), store) if err != nil { t.Fatalf("Couldn't create monitor: %v", err) } - // Initialization and CreateEpoch is called by NewEnv mcc := mupb.NewMutationServiceClient(env.Conn) mutCli := client.New(mcc, time.Second) - // verify first SMR - mutResp, err := mutCli.PollMutations(bctx, 1) - if err != nil { - t.Fatalf("Could not query mutations: %v", err) - } - if err := mon.Process(mutResp); err != nil { - t.Fatalf("Monitor could process mutations: %v", err) - } - mresp, err := store.Get(1) - if err != nil { - t.Fatalf("Could not read monitoring response: %v", err) - } - for _, err := range mresp.Errors { - t.Errorf("Got error: %v", err) - } - // client sends one mutation, sequencer "signs", monitor verifies - userID := "test@test.com" - signers := []signatures.Signer{createSigner(t, testPrivKey1)} - authorizedKeys := []*kpb.PublicKey{getAuthorizedKey(testPubKey1)} + for _, tc := range []struct { + // the userIDs to update, if no userIDs are provided, no update request + // will be send before querying + userIDs []string + updateData []byte + signers []signatures.Signer + authorizedKeys []*kpb.PublicKey + // the epoch to query after sending potential updates + queryEpoch int64 + }{ + // query first epoch, don't update + {[]string{}, nil, nil, nil, 1}, + // create one mutation and new epoch (not forced like in sequencer): + {[]string{"test@test.com"}, []byte("testData"), []signatures.Signer{createSigner(t, testPrivKey1)}, []*kpb.PublicKey{getAuthorizedKey(testPubKey1)}, 2}, + // create several mutations and new epoch + {[]string{"test@test.com", "test2@test2.com"}, []byte("more update data"), []signatures.Signer{createSigner(t, testPrivKey1)}, []*kpb.PublicKey{getAuthorizedKey(testPubKey1)}, 3}, + } { + for _, userID := range tc.userIDs { + _, err = env.Client.Update(GetNewOutgoingContextWithFakeAuth(userID), + userID, appID, tc.updateData, tc.signers, tc.authorizedKeys) + if err != grpcc.ErrRetry { + t.Fatalf("Could not send update request: %v", err) + } + } - _, err = env.Client.Update(GetNewOutgoingContextWithFakeAuth("test@test.com"), userID, appID, []byte("testProfile"), signers, authorizedKeys) - if err != grpcc.ErrRetry { - t.Fatalf("Could not send update request: %v", err) - } - if err := env.Signer.CreateEpoch(bctx, false); err != nil { - t.Errorf("CreateEpoch(_): %v", err) - } - mutResp, err = mutCli.PollMutations(bctx, 2) - if err != nil { - t.Fatalf("Could not query mutations: %v", err) - } - if err := mon.Process(mutResp); err != nil { - t.Fatalf("Monitor could not process mutations: %v", err) - } - mresp, err = store.Get(2) - if err != nil { - t.Fatalf("Could not read monitoring response: %v", err) - } - for _, err := range mresp.Errors { - t.Errorf("Got error: %v", err) + if err := env.Signer.CreateEpoch(bctx, false); err != nil { + t.Fatalf("CreateEpoch(_): %v", err) + } + + mutResp, err := mutCli.PollMutations(bctx, tc.queryEpoch) + if err != nil { + t.Fatalf("Could not query mutations: %v", err) + } + + if err := mon.Process(mutResp); err != nil { + t.Fatalf("Monitor could not process mutations: %v", err) + } + + mresp, err := store.Get(tc.queryEpoch) + if err != nil { + t.Fatalf("Could not read monitoring response: %v", err) + } + + for _, err := range mresp.Errors { + t.Errorf("Got error: %v", err) + } } } diff --git a/integration/testutil.go b/integration/testutil.go index 172b3915f..9b5d53412 100644 --- a/integration/testutil.go +++ b/integration/testutil.go @@ -138,11 +138,6 @@ func NewEnv(t *testing.T) *Env { logTree, err := mapEnv.AdminClient.CreateTree(ctx, &trillian.CreateTreeRequest{ Tree: stestonly.LogTree, }) - //logPubKey, err := der.UnmarshalPublicKey(tree.GetPublicKey().GetDer()) - //if err != nil { - // t.Fatalf("Failed to load signing keypair: %v", err) - //} - if err != nil { t.Fatalf("CreateTree(): %v", err) } From d1e6aea99dc9a0f3cde43dd2f115d0ad9e5154d4 Mon Sep 17 00:00:00 2001 From: Liamsi Date: Thu, 31 Aug 2017 20:27:28 +0100 Subject: [PATCH 10/10] nits (imports) and contributors file --- CONTRIBUTORS | 1 + core/monitor/verify.go | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 9559221c3..d59f068bd 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -13,3 +13,4 @@ Antonio Marcedone Cesar Ghali Daniel Ziegler Gary Belvin +Ismail Khoffi diff --git a/core/monitor/verify.go b/core/monitor/verify.go index 9f13f138c..66648ebbf 100644 --- a/core/monitor/verify.go +++ b/core/monitor/verify.go @@ -23,14 +23,13 @@ import ( "errors" "math/big" - "github.com/golang/glog" + "github.com/google/keytransparency/core/mutator/entry" + "github.com/golang/glog" + tcrypto "github.com/google/trillian/crypto" "github.com/google/trillian/merkle" "github.com/google/trillian/storage" - tcrypto "github.com/google/trillian/crypto" - - "github.com/google/keytransparency/core/mutator/entry" ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types" )