Skip to content

Commit

Permalink
Merge branch 'master' into reduce
Browse files Browse the repository at this point in the history
* master:
  (prexisting) remove unused vars
  Fix mutation test
  Check for nil keyset
  Use IsValidEntry in Keyserver
  Separate out IsValidEntry helper
  Made logical split for mutation validation function
  update travis.yml to xenial (google#1215)
  • Loading branch information
gdbelvin committed Feb 22, 2019
2 parents 01c84bc + ae85001 commit da5f616
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 74 deletions.
14 changes: 4 additions & 10 deletions .travis.yml
@@ -1,23 +1,18 @@
language: go language: go
go: go:
- 1.x - 1.x
dist: trusty dist: xenial
services:
- docker-ce go_import_path: github.com/google/keytransparency

cache: cache:
directories: directories:
- "$HOME/gcloud/" - "$HOME/gcloud/"
env: env:
global: global:
- DOCKER_COMPOSE_VERSION="1.13.0"
- PATH=$PATH:${HOME}/google-cloud-sdk/bin - PATH=$PATH:${HOME}/google-cloud-sdk/bin
- CLOUDSDK_CORE_DISABLE_PROMPTS=1 - CLOUDSDK_CORE_DISABLE_PROMPTS=1


addons:
apt:
packages:
- python3-pip

before_install: before_install:
- | - |
if [ ! -d $HOME/gcloud/google-cloud-sdk ]; then if [ ! -d $HOME/gcloud/google-cloud-sdk ]; then
Expand Down Expand Up @@ -47,7 +42,6 @@ after_success:
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)


before_deploy: before_deploy:
- pip3 install -U --user docker-compose
- docker --version - docker --version
- docker-compose --version - docker-compose --version
- openssl aes-256-cbc -K $encrypted_555d9b2948d2_key -iv $encrypted_555d9b2948d2_iv - openssl aes-256-cbc -K $encrypted_555d9b2948d2_key -iv $encrypted_555d9b2948d2_iv
Expand Down
4 changes: 2 additions & 2 deletions cmd/keytransparency-sequencer/main.go
Expand Up @@ -195,13 +195,13 @@ func main() {
go serveHTTPGateway(ctx, lis, dopts, grpcServer, go serveHTTPGateway(ctx, lis, dopts, grpcServer,
pb.RegisterKeyTransparencyAdminHandlerFromEndpoint, pb.RegisterKeyTransparencyAdminHandlerFromEndpoint,
) )
runSequencer(ctx, conn, mconn, directoryStorage) runSequencer(ctx, conn, directoryStorage)


// Shutdown. // Shutdown.
glog.Errorf("Signer exiting") glog.Errorf("Signer exiting")
} }


func runSequencer(ctx context.Context, conn, mconn *grpc.ClientConn, func runSequencer(ctx context.Context, conn *grpc.ClientConn,
directoryStorage dir.Storage) { directoryStorage dir.Storage) {
electionFactory, closeFactory := getElectionFactory() electionFactory, closeFactory := getElectionFactory()
defer closeFactory() defer closeFactory()
Expand Down
2 changes: 1 addition & 1 deletion cmd/keytransparency-server/main.go
Expand Up @@ -121,7 +121,7 @@ func main() {
tmap := trillian.NewTrillianMapClient(mconn) tmap := trillian.NewTrillianMapClient(mconn)


// Create gRPC server. // Create gRPC server.
ksvr := keyserver.New(tlog, tmap, entry.ReduceFn, directories, logs, logs, ksvr := keyserver.New(tlog, tmap, entry.IsValidEntry, directories, logs, logs,
prometheus.MetricFactory{}) prometheus.MetricFactory{})
grpcServer := grpc.NewServer( grpcServer := grpc.NewServer(
grpc.Creds(creds), grpc.Creds(creds),
Expand Down
38 changes: 20 additions & 18 deletions core/keyserver/keyserver.go
Expand Up @@ -81,33 +81,33 @@ type indexFunc func(ctx context.Context, d *directory.Directory, userID string)


// Server holds internal state for the key server. // Server holds internal state for the key server.
type Server struct { type Server struct {
tlog tpb.TrillianLogClient tlog tpb.TrillianLogClient
tmap tpb.TrillianMapClient tmap tpb.TrillianMapClient
mutate mutator.ReduceMutationFn verifyMutation mutator.VerifyMutationFn
directories directory.Storage directories directory.Storage
logs MutationLogs logs MutationLogs
batches BatchReader batches BatchReader
indexFunc indexFunc indexFunc indexFunc
} }


// New creates a new instance of the key server. // New creates a new instance of the key server.
func New(tlog tpb.TrillianLogClient, func New(tlog tpb.TrillianLogClient,
tmap tpb.TrillianMapClient, tmap tpb.TrillianMapClient,
mutate mutator.ReduceMutationFn, verifyMutation mutator.VerifyMutationFn,
directories directory.Storage, directories directory.Storage,
logs MutationLogs, logs MutationLogs,
batches BatchReader, batches BatchReader,
metricsFactory monitoring.MetricFactory, metricsFactory monitoring.MetricFactory,
) *Server { ) *Server {
initMetrics.Do(func() { createMetrics(metricsFactory) }) initMetrics.Do(func() { createMetrics(metricsFactory) })
return &Server{ return &Server{
tlog: tlog, tlog: tlog,
tmap: tmap, tmap: tmap,
mutate: mutate, verifyMutation: verifyMutation,
directories: directories, directories: directories,
logs: logs, logs: logs,
batches: batches, batches: batches,
indexFunc: indexFromVRF, indexFunc: indexFromVRF,
} }
} }


Expand Down Expand Up @@ -563,14 +563,16 @@ func (s *Server) BatchQueueUserUpdate(ctx context.Context, in *pb.BatchQueueUser
// - Correct profile commitment. // - Correct profile commitment.
// - Correct key formats. // - Correct key formats.
for _, u := range in.Updates { for _, u := range in.Updates {
if err := validateEntryUpdate(u, vrfPriv); err != nil { if err := s.verifyMutation(u.Mutation); err != nil {
glog.Warningf("Invalid UpdateEntryRequest: %v", err)
return nil, status.Errorf(codes.InvalidArgument, "Invalid mutation")
}
if err = validateEntryUpdate(u, vrfPriv); err != nil {
glog.Warningf("Invalid UpdateEntryRequest: %v", err) glog.Warningf("Invalid UpdateEntryRequest: %v", err)
return nil, status.Errorf(codes.InvalidArgument, "Invalid request") return nil, status.Errorf(codes.InvalidArgument, "Invalid request")
} }
} }


// TODO(#1177): Verify parts of the mutation that don't reference the current map value here.

// Save mutation to the database. // Save mutation to the database.
wm, err := s.logs.Send(ctx, directory.DirectoryID, in.Updates...) wm, err := s.logs.Send(ctx, directory.DirectoryID, in.Updates...)
if err != nil { if err != nil {
Expand Down
18 changes: 2 additions & 16 deletions core/mutator/entry/mutation.go
Expand Up @@ -19,12 +19,9 @@ import (
"fmt" "fmt"


"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/google/keytransparency/core/crypto/commitments"
"github.com/google/tink/go/keyset" "github.com/google/tink/go/keyset"
"github.com/google/tink/go/tink" "github.com/google/tink/go/tink"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/google/keytransparency/core/crypto/commitments"


pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto" pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto"
tinkpb "github.com/google/tink/proto/tink_go_proto" tinkpb "github.com/google/tink/proto/tink_go_proto"
Expand All @@ -37,7 +34,6 @@ type Mutation struct {
UserID string UserID string
data, nonce []byte data, nonce []byte


prevEntry *pb.Entry
prevSignedEntry *pb.SignedEntry prevSignedEntry *pb.SignedEntry
entry *pb.Entry entry *pb.Entry
signedEntry *pb.SignedEntry signedEntry *pb.SignedEntry
Expand Down Expand Up @@ -113,19 +109,9 @@ func (m *Mutation) SerializeAndSign(signers []tink.Signer) (*pb.EntryUpdate, err
return nil, err return nil, err
} }


// Check authorization.
if err := verifyKeys(m.prevEntry.GetAuthorizedKeys(), m.entry.GetAuthorizedKeys(),
mutation.Entry, mutation.Signatures); err != nil {
return nil, status.Errorf(codes.PermissionDenied,
"verifyKeys(oldKeys: %v, newKeys: %v, sigs: %v): %v",
len(m.prevEntry.GetAuthorizedKeys().GetKey()),
len(m.entry.GetAuthorizedKeys().GetKey()),
len(mutation.GetSignatures()), err)
}

// Sanity check the mutation's correctness. // Sanity check the mutation's correctness.
if _, err := MutateFn(m.prevSignedEntry, mutation); err != nil { if _, err := MutateFn(m.prevSignedEntry, mutation); err != nil {
return nil, fmt.Errorf("presign mutation check: %v", err) return nil, err
} }


return &pb.EntryUpdate{ return &pb.EntryUpdate{
Expand Down
57 changes: 37 additions & 20 deletions core/mutator/entry/mutator.go
Expand Up @@ -17,14 +17,15 @@ package entry
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"errors"
"fmt" "fmt"


"github.com/google/keytransparency/core/mutator" "github.com/golang/glog"
"github.com/golang/protobuf/proto"
"github.com/google/tink/go/keyset" "github.com/google/tink/go/keyset"
"github.com/google/tink/go/signature" "github.com/google/tink/go/signature"


"github.com/golang/glog" "github.com/google/keytransparency/core/mutator"
"github.com/golang/protobuf/proto"


pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto" pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto"
tinkpb "github.com/google/tink/proto/tink_go_proto" tinkpb "github.com/google/tink/proto/tink_go_proto"
Expand All @@ -44,6 +45,23 @@ func MapLogItemFn(m *mutator.LogMessage, emit func(index []byte, mutation *pb.En
return nil return nil
} }


// IsValidEntry checks the internal consistency and correctness of a mutation.
func IsValidEntry(signedEntry *pb.SignedEntry) error {
// Ensure that the mutation size is within bounds.
if got, want := proto.Size(signedEntry), mutator.MaxMutationSize; got > want {
glog.Warningf("mutation is %v bytes, want <= %v", got, want)
return mutator.ErrSize
}

newEntry := pb.Entry{}
if err := proto.Unmarshal(signedEntry.GetEntry(), &newEntry); err != nil {
return fmt.Errorf("proto.Unmarshal(): %v", err)
}

ks := newEntry.GetAuthorizedKeys()
return verifyKeys(ks, signedEntry.GetEntry(), signedEntry.GetSignatures())
}

// ReduceFn decides which of multiple updates can be applied in this revision. // ReduceFn decides which of multiple updates can be applied in this revision.
func ReduceFn(index []byte, msgs []*pb.EntryUpdate, leaves []*tpb.MapLeaf, emit func(*tpb.MapLeaf)) error { func ReduceFn(index []byte, msgs []*pb.EntryUpdate, leaves []*tpb.MapLeaf, emit func(*tpb.MapLeaf)) error {
if got := len(leaves); got > 1 { if got := len(leaves); got > 1 {
Expand Down Expand Up @@ -87,17 +105,16 @@ func ReduceFn(index []byte, msgs []*pb.EntryUpdate, leaves []*tpb.MapLeaf, emit
// MutateFn verifies that newSignedEntry is a valid mutation for oldSignedEntry and returns the // MutateFn verifies that newSignedEntry is a valid mutation for oldSignedEntry and returns the
// application of newSignedEntry to oldSignedEntry. // application of newSignedEntry to oldSignedEntry.
func MutateFn(oldSignedEntry, newSignedEntry *pb.SignedEntry) (*pb.SignedEntry, error) { func MutateFn(oldSignedEntry, newSignedEntry *pb.SignedEntry) (*pb.SignedEntry, error) {
// Ensure that the mutation size is within bounds. if err := IsValidEntry(newSignedEntry); err != nil {
if got, want := proto.Size(newSignedEntry), mutator.MaxMutationSize; got > want { return nil, err
glog.Warningf("mutation is %v bytes, want <= %v", got, want)
return nil, mutator.ErrSize
} }


newEntry := pb.Entry{} newEntry := pb.Entry{}
if err := proto.Unmarshal(newSignedEntry.GetEntry(), &newEntry); err != nil { if err := proto.Unmarshal(newSignedEntry.GetEntry(), &newEntry); err != nil {
return nil, fmt.Errorf("proto.Unmarshal(): %v", err) return nil, fmt.Errorf("proto.Unmarshal(): %v", err)
} }
oldEntry := pb.Entry{} oldEntry := pb.Entry{}
// oldSignedEntry may be nil, resulting an empty oldEntry struct.
if err := proto.Unmarshal(oldSignedEntry.GetEntry(), &oldEntry); err != nil { if err := proto.Unmarshal(oldSignedEntry.GetEntry(), &oldEntry); err != nil {
return nil, fmt.Errorf("proto.Unmarshal(): %v", err) return nil, fmt.Errorf("proto.Unmarshal(): %v", err)
} }
Expand All @@ -107,17 +124,21 @@ func MutateFn(oldSignedEntry, newSignedEntry *pb.SignedEntry) (*pb.SignedEntry,
prevEntryHash := sha256.Sum256(oldSignedEntry.GetEntry()) prevEntryHash := sha256.Sum256(oldSignedEntry.GetEntry())
if got, want := prevEntryHash[:], newEntry.GetPrevious(); !bytes.Equal(got, want) { if got, want := prevEntryHash[:], newEntry.GetPrevious(); !bytes.Equal(got, want) {
// Check if this mutation is a replay. // Check if this mutation is a replay.
if oldSignedEntry != nil && if bytes.Equal(oldSignedEntry.GetEntry(), newSignedEntry.Entry) {
bytes.Equal(oldSignedEntry.Entry, newSignedEntry.Entry) {
glog.Warningf("mutation is a replay of an old one") glog.Warningf("mutation is a replay of an old one")
return nil, mutator.ErrReplay return nil, mutator.ErrReplay
} }
glog.Warningf("previous entry hash: %x, want %x", got, want) glog.Warningf("previous entry hash: %x, want %x", got, want)
return nil, mutator.ErrPreviousHash return nil, mutator.ErrPreviousHash
} }


if err := verifyKeys(oldEntry.GetAuthorizedKeys(), newEntry.GetAuthorizedKeys(), if oldSignedEntry == nil {
newSignedEntry.Entry, newSignedEntry.GetSignatures()); err != nil { // Skip verificaion checks if there is no previous oldSignedEntry.
return newSignedEntry, nil
}

ks := oldEntry.GetAuthorizedKeys()
if err := verifyKeys(ks, newSignedEntry.Entry, newSignedEntry.GetSignatures()); err != nil {
return nil, err return nil, err
} }


Expand All @@ -126,16 +147,12 @@ func MutateFn(oldSignedEntry, newSignedEntry *pb.SignedEntry) (*pb.SignedEntry,


// verifyKeys verifies both old and new authorized keys based on the following // verifyKeys verifies both old and new authorized keys based on the following
// criteria: // criteria:
// 1. At least one signature with a key in the previous entry should exist. // 1. At least one signature with a key in the entry should exist.
// 2. If prevAuthz is nil, at least one signature with a key from the new // 2. Signatures with no matching keys are simply ignored.
// authorized_key set should exist. func verifyKeys(ks *tinkpb.Keyset, data []byte, sigs [][]byte) error {
// 3. Signatures with no matching keys are simply ignored. if ks == nil {
func verifyKeys(prevAuthz, authz *tinkpb.Keyset, data []byte, sigs [][]byte) error { return errors.New("entry: nil keyset")
ks := prevAuthz
if prevAuthz == nil {
ks = authz
} }

handle, err := keyset.NewHandleWithNoSecrets(ks) handle, err := keyset.NewHandleWithNoSecrets(ks)
if err != nil { if err != nil {
return fmt.Errorf("tink.KeysetHanldeWithNoSecret(new): %v", err) return fmt.Errorf("tink.KeysetHanldeWithNoSecret(new): %v", err)
Expand Down
14 changes: 9 additions & 5 deletions core/mutator/entry/mutator_test.go
Expand Up @@ -109,7 +109,8 @@ func TestCheckMutation(t *testing.T) {
AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(), AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(),
}, },
}, },
err: mutator.ErrReplay, signers: testutil.SignKeysetsFromPEMs(testPrivKey1, testPrivKey2),
err: mutator.ErrReplay,
}, },
{ {
desc: "Large mutation", desc: "Large mutation",
Expand All @@ -132,16 +133,19 @@ func TestCheckMutation(t *testing.T) {
AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(), AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(),
}, },
}, },
err: mutator.ErrPreviousHash, signers: testutil.SignKeysetsFromPEMs(testPrivKey1, testPrivKey2),
err: mutator.ErrPreviousHash,
}, },
{ {
desc: "Very first mutation, invalid previous entry hash", desc: "Very first mutation, invalid previous entry hash",
mutation: &Mutation{ mutation: &Mutation{
entry: &tpb.Entry{ entry: &tpb.Entry{
Previous: nil, Previous: nil,
AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(),
}, },
}, },
err: mutator.ErrPreviousHash, signers: testutil.SignKeysetsFromPEMs(testPrivKey2),
err: mutator.ErrPreviousHash,
}, },
{ {
desc: "Very first mutation, missing previous signature", desc: "Very first mutation, missing previous signature",
Expand All @@ -168,7 +172,7 @@ func TestCheckMutation(t *testing.T) {
AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(), AuthorizedKeys: testutil.VerifyKeysetFromPEMs(testPubKey2).Keyset(),
}, },
}, },
signers: testutil.SignKeysetsFromPEMs(testPrivKey1), signers: testutil.SignKeysetsFromPEMs(testPrivKey1, testPrivKey2),
}, },
} { } {
m, err := tc.mutation.sign(tc.signers) m, err := tc.mutation.sign(tc.signers)
Expand Down
8 changes: 7 additions & 1 deletion core/mutator/mutator.go
Expand Up @@ -20,6 +20,9 @@ package mutator
import ( import (
"errors" "errors"


"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto" pb "github.com/google/keytransparency/core/api/v1/keytransparency_go_proto"
tpb "github.com/google/trillian" tpb "github.com/google/trillian"
) )
Expand All @@ -42,9 +45,12 @@ var (
ErrInvalidSig = errors.New("mutation: invalid signature") ErrInvalidSig = errors.New("mutation: invalid signature")
// ErrUnauthorized occurs when the mutation has not been signed by a key in the // ErrUnauthorized occurs when the mutation has not been signed by a key in the
// previous entry. // previous entry.
ErrUnauthorized = errors.New("mutation: unauthorized") ErrUnauthorized = status.Errorf(codes.PermissionDenied, "mutation: unauthorized")
) )


// VerifyMutationFn verifies that a mutation is internally consistent.
type VerifyMutationFn func(mutation *pb.SignedEntry) error

// ReduceMutationFn takes all the mutations for an index an an auxiliary input // ReduceMutationFn takes all the mutations for an index an an auxiliary input
// of existing mapleaf(s) returns a new map leaf. ReduceMutationFn must be // of existing mapleaf(s) returns a new map leaf. ReduceMutationFn must be
// idempotent, communative, and associative. i.e. must produce the same output // idempotent, communative, and associative. i.e. must produce the same output
Expand Down
2 changes: 1 addition & 1 deletion impl/integration/env.go
Expand Up @@ -181,7 +181,7 @@ func NewEnv(ctx context.Context) (*Env, error) {


pb.RegisterKeyTransparencyServer(gsvr, keyserver.New( pb.RegisterKeyTransparencyServer(gsvr, keyserver.New(
logEnv.Log, mapEnv.Map, logEnv.Log, mapEnv.Map,
entry.ReduceFn, directoryStorage, entry.IsValidEntry, directoryStorage,
mutations, mutations, mutations, mutations,
monitoring.InertMetricFactory{}, monitoring.InertMetricFactory{},
)) ))
Expand Down

0 comments on commit da5f616

Please sign in to comment.