diff --git a/cmd/keytransparency-monitor/main.go b/cmd/keytransparency-monitor/main.go
index b76685507..a209da21d 100644
--- a/cmd/keytransparency-monitor/main.go
+++ b/cmd/keytransparency-monitor/main.go
@@ -42,18 +42,22 @@ 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
+ "html/template"
)
var (
addr = flag.String("addr", ":8099", "The ip:port combination to listen on")
- keyFile = flag.String("tls-key", "genfiles/server.key", "TLS private key file")
- certFile = flag.String("tls-cert", "genfiles/server.pem", "TLS cert file")
+ keyFile = flag.String("tls-key", "../../genfiles/server.key", "TLS private key file")
+ certFile = flag.String("tls-cert", "../../genfiles/server.pem", "TLS cert file")
- signingKey = flag.String("sign-key", "genfiles/monitor_sign-key.pem", "Path to private key PEM for SMH signing")
+ signingKey = flag.String("sign-key", "../../genfiles/monitor_sign-key.pem", "Path to private key PEM for SMH signing")
signingKeyPassword = flag.String("password", "towel", "Password of the private key PEM file for SMH signing")
- ktURL = flag.String("kt-url", "localhost:8080", "URL of key-server.")
+ ktURL = flag.String("kt-url", "35.184.134.53:8080", "URL of key-server.")
insecure = flag.Bool("insecure", false, "Skip TLS checks")
ktCert = flag.String("kt-cert", "genfiles/server.crt", "Path to kt-server's public key")
@@ -65,11 +69,11 @@ var (
func grpcGatewayMux(addr string) (*runtime.ServeMux, error) {
ctx := context.Background()
- creds, err := credentials.NewClientTLSFromFile(*certFile, "")
- if err != nil {
- return nil, err
- }
- dopts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
+ //creds, err := credentials.NewClientTLSFromFile(*certFile, "")
+ //if err != nil {
+ // return nil, err
+ //}
+ dopts := []grpc.DialOption{grpc.WithInsecure()}
gwmux := runtime.NewServeMux()
if err := mopb.RegisterMonitorServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts); err != nil {
return nil, err
@@ -92,19 +96,47 @@ func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Ha
})
}
-func main() {
- flag.Parse()
+// Hackathon only code. Remove later!
+type resHandler struct {
+ store *storage.Storage
+}
- creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
+func (h *resHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ max := h.store.LatestEpoch()
+ results := make([]*storage.MonitoringResult, max)
+ for i:=int64(1); i<= max ; i++ {
+ monRes, err := h.store.Get(i)
+ if err != nil {
+ glog.Errorf("Couldn't retrieve mon result: %v", err)
+ }
+ results[i-1] = monRes
+ }
+ // TODO(ismail) make this file path configurable so that it can be found in
+ // docker as well
+ tmpl, err := template.ParseFiles("/Users/khoffi/go/src/github.com/google/keytransparency/cmd/keytransparency-monitor/web/monitoring.tmpl")
+ if err != nil {
+ glog.Errorf("Could not parse template: %v", err)
+ }
+
+ err = tmpl.Execute(w, results)
if err != nil {
- glog.Exitf("Failed to load server credentials %v", err)
+ glog.Errorf("Could not write result: %v", err)
}
+}
+
+func main() {
+ flag.Parse()
+
+ //creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
+ //if err != nil {
+ // glog.Exitf("Failed to load server credentials %v", err)
+ //}
// Create gRPC server.
grpcServer := grpc.NewServer(
- grpc.Creds(creds),
+ //grpc.Creds(creds),
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
- grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
+ //grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
)
// Connect to the kt-server's mutation API:
@@ -140,14 +172,27 @@ func main() {
// Insert handlers for other http paths here.
mux := http.NewServeMux()
+
+ resultHandler := &resHandler{store:store}
+ mux.Handle("/monitor", resultHandler)
+
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() {
@@ -169,7 +214,7 @@ func main() {
// Serve HTTP2 server over TLS.
glog.Infof("Listening on %v", *addr)
- if err := http.ListenAndServeTLS(*addr, *certFile, *keyFile,
+ if err := http.ListenAndServe(*addr, /**certFile, *keyFile,*/
grpcHandlerFunc(grpcServer, mux)); err != nil {
glog.Errorf("ListenAndServeTLS: %v", err)
}
diff --git a/cmd/keytransparency-monitor/web/monitoring.tmpl b/cmd/keytransparency-monitor/web/monitoring.tmpl
new file mode 100644
index 000000000..419ccfb9b
--- /dev/null
+++ b/cmd/keytransparency-monitor/web/monitoring.tmpl
@@ -0,0 +1,19 @@
+{{range .}}
+
+ | Epoch: {{.Response.Epoch}} |
+ | Map-id: {{.Response.Smr.MapId}} |
+ | Log-id: {{.Response.LogRoot.LogId}} |
+ | SMR roothash: {{.Response.Smr.RootHash}} |
+ | |
+ | Errors: |
+ {{range .Errors}}
+
+ | Error: {{.}} |
+
+ {{end}}
+
+
+
+
+
+{{end}}
\ No newline at end of file
diff --git a/core/monitor/monitor.go b/core/monitor/monitor.go
index 419936b28..f1981ec3b 100644
--- a/core/monitor/monitor.go
+++ b/core/monitor/monitor.go
@@ -25,39 +25,39 @@ 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/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 {
- hasher hashers.MapHasher
- logPubKey crypto.PublicKey
+ mapID int64
+ mapHasher hashers.MapHasher
mapPubKey crypto.PublicKey
- logVerifier merkle.LogVerifier
+ logVerifier client.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.
-func New(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)
}
+ mapPubKey, err := der.UnmarshalPublicKey(mapTree.GetPublicKey().GetDer())
+ if err != nil {
+ return nil, fmt.Errorf("Could not unmarshal map public key: %v", err)
+ }
return &Monitor{
- hasher: mapHasher,
- logVerifier: merkle.NewLogVerifier(logHasher),
- logPubKey: logTree.GetPublicKey(),
- mapPubKey: mapTree.GetPublicKey(),
+ logVerifier: logverifierCli,
+ mapID: mapTree.TreeId,
+ mapHasher: mapHasher,
+ mapPubKey: mapPubKey,
signer: signer,
store: store,
}, nil
@@ -70,7 +70,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 1deadfb45..e477786cd 100644
--- a/core/monitor/verify.go
+++ b/core/monitor/verify.go
@@ -18,14 +18,195 @@
package monitor
import (
+ "bytes"
+ "encoding/json"
+ "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")
+ // 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")
+)
+
+// 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. If any verification check failed it returns
-// an error.
-func (m *Monitor) verifyMutationsResponse(in *ktpb.GetMutationsResponse) []error {
+// because of the max. page size.
+func (m *Monitor) VerifyMutationsResponse(in *ktpb.GetMutationsResponse) []error {
+ errList := make([]error, 0)
+
+ //
+ // log verification
+ //
+ if m.trusted == nil {
+ m.trusted = in.GetLogRoot()
+ }
+
+ if err := m.logVerifier.VerifyRoot(m.trusted, in.GetLogRoot(), in.GetLogConsistency()); err != nil {
+ // this could be one of ErrInvalidLogSignature, ErrInvalidLogConsistencyProof
+ errList = append(errList, err)
+ }
+ // updated trusted log root
+ m.trusted = in.GetLogRoot()
+
+ b, err := json.Marshal(in.GetSmr())
+ if err != nil {
+ glog.Errorf("json.Marshal(): %v", err)
+ errList = append(errList, ErrInvalidMapSignature)
+ }
+ leafIndex := in.GetSmr().GetMapRevision()
+ treeSize := in.GetLogRoot().GetTreeSize()
+ err = m.logVerifier.VerifyInclusionAtIndex(in.GetLogRoot(), b, leafIndex, in.GetLogInclusion())
+ if err != nil {
+ glog.Errorf("m.logVerifier.VerifyInclusionAtIndex((%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)
+ }
+
+ // 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 {
+ // 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...)
+ }
+ } // TODO else oldRoot is the hash of the initial empty sparse merkle tree
+
+ 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))
+ glog.Infof("verifyMutations() called with %v mutations.", 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()
+ leaf := mut.GetProof().GetLeaf().GetLeafValue()
+ if err := merkle.VerifyMapInclusionProof(mapID, index,
+ leaf, oldRoot, mut.GetProof().GetInclusion(), m.mapHasher); err != nil {
+ glog.Infof("VerifyMapInclusionProof(%x): %v", index, err)
+ errList = append(errList, ErrInvalidMutation)
+ }
+
+ // compute the new leaf
+ 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())
+ newLeafHash := m.mapHasher.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()
+ 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) {
+ // this is really odd and should never happen
+ errList = append(errList, ErrInconsistentProofs)
+ }
+ } else {
+ if len(proof) > 0 {
+ oldProofNodes[sibID.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.mapHasher)
+ 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 {
+ return p, nil
+ }
+ return nil, nil
+ }, nil)
+
+ if err != nil {
+ glog.Errorf("hs2.HStar2Nodes(_): %v", err)
+ return ErrNotMatchingMapRoot
+ }
+
+ // verify rootHash
+ if !bytes.Equal(newRoot, expectedRoot) {
+ return ErrNotMatchingMapRoot
+ }
+
return nil
}
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/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..483a80471
--- /dev/null
+++ b/integration/monitor_test.go
@@ -0,0 +1,113 @@
+// 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"
+ 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"
+ "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 (
+ monitorPrivKey = `-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAV7H3qRi/cj/w04vEQBFjLdhcXRbZR4ouT5zaAy1XUHoAoGCCqGSM49
+AwEHoUQDQgAEqUDbATN2maGIm6YQLpjx67bYN1hxPPdF0VrPTZe36yQhH+GCwZQV
+amFdON6OhjYnBmJWe4fVnbxny0PfpkvXtg==
+-----END EC PRIVATE KEY-----`
+)
+
+func TestMonitor(t *testing.T) {
+ bctx := context.Background()
+ env := NewEnv(t)
+ defer env.Close(t)
+ env.Client.RetryCount = 0
+
+ // setup monitor:
+ 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()
+ // 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)}
+
+ _, 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)
+ }
+}
diff --git a/integration/testutil.go b/integration/testutil.go
index 5a0198f3e..172b3915f 100644
--- a/integration/testutil.go
+++ b/integration/testutil.go
@@ -43,14 +43,13 @@ 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"
stestonly "github.com/google/trillian/storage/testonly"
)
-const (
- logID = 0
-)
-
// NewDB creates a new in-memory database for testing.
func NewDB(t testing.TB) *sql.DB {
db, err := sql.Open("sqlite3", "file:dummy.db?mode=memory&cache=shared")
@@ -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)
diff --git a/scripts/configure_trillian.sh b/scripts/configure_trillian.sh
index 34d3de110..99c2adb58 100644
--- a/scripts/configure_trillian.sh
+++ b/scripts/configure_trillian.sh
@@ -3,8 +3,30 @@
# Defaults from docker-compose.yml.
LOG_URL=localhost:8091
MAP_URL=localhost:8094
+KT_URL=localhost:8080
+
LOCAL=true
+function retrieveTrees()
+{
+ if [ "$LOCAL" = true ]; then
+ JSON=`curl -f http://${KT_URL}/v1/domain/info`
+ else
+ KTSRV=$(kubectl get pods --selector=run=kt-server -o jsonpath={.items[*].metadata.name})
+ JSON=`kubectl exec -i ${KTSRV} -- curl http://kt-server:8080/v1/domain/info`
+ fi
+
+ export LOG_ID=`echo ${JSON} | jq -r '.log.tree_id'`
+ echo "-----BEGIN PUBLIC KEY-----" > genfiles/trillian-log.pem
+ echo ${JSON} | jq -r '.log.public_key.der' | cat >> genfiles/trillian-log.pem
+ echo "-----END PUBLIC KEY-----" >> genfiles/trillian-log.pem
+
+ export MAP_ID=`echo ${JSON} | jq -r '.map.tree_id'`
+ echo "-----BEGIN PUBLIC KEY-----" > genfiles/trillian-map.pem
+ echo ${JSON} | jq -r '.map.public_key.der' | cat >> genfiles/trillian-map.pem
+ echo "-----END PUBLIC KEY-----" >> genfiles/trillian-map.pem
+}
+
function createLog()
{
if [ "$LOCAL" = true ]; then
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
index 95b8dfda7..b1c6c254d 100755
--- a/scripts/deploy.sh
+++ b/scripts/deploy.sh
@@ -40,7 +40,7 @@ function main()
pushKTImgs
waitForTrillian
- createTreeAndSetIDs
+ createTreeIfNeccessaryAndSetIDs
# Need to (re)build kt-signer after writing the public-keys
docker-compose build kt-signer
@@ -103,16 +103,21 @@ function waitForTrillian()
fi
}
-function createTreeAndSetIDs()
+function createTreeIfNeccessaryAndSetIDs()
{
- LOG_ID=""
- MAP_ID=""
+ # first request domain info:
+ retrieveTrees
+
+ if [ -n "$LOG_ID" ] && [ -n "$MAP_ID" ]; then
+ echo "Using existing trees with MAP_ID=$MAP_ID and LOG_ID=$LOG_ID"
+ fi
+
COUNTER=0
+ export LOCAL=false;
until [ -n "$LOG_ID" ] || [ $COUNTER -gt $MAX_RETRY ]; do
# RPC was not available yet, wait and retry:
sleep 10;
let COUNTER+=1;
- export LOCAL=false;
createLog && createMap
done