Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TrustStore: Implement inserter #3225

Merged
merged 1 commit into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions go/lib/infra/modules/trust/v2/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@ go_test(
name = "go_default_test",
srcs = [
"export_test.go",
"inserter_test.go",
"inspector_test.go",
"main_test.go",
"provider_test.go",
"recurser_test.go",
"resolver_test.go",
"router_test.go",
],
data = [
"//go/lib/infra/modules/trust/v2/testdata:crypto_tar",
],
embed = [":go_default_library"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/infra:go_default_library",
"//go/lib/infra/modules/trust/v2/internal/decoded:go_default_library",
"//go/lib/infra/modules/trust/v2/mock_v2:go_default_library",
Expand All @@ -52,6 +55,8 @@ go_test(
"//go/lib/scrypto/trc/v2:go_default_library",
"//go/lib/serrors:go_default_library",
"//go/lib/snet:go_default_library",
"//go/lib/snet/mock_snet:go_default_library",
"//go/lib/spath:go_default_library",
"//go/lib/util:go_default_library",
"//go/lib/xtest:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
Expand Down
55 changes: 53 additions & 2 deletions go/lib/infra/modules/trust/v2/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,30 @@

package trust

import (
"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/snet"
)

var (
// NewCryptoProvider allows instantiating the private cryptoProvider for
// black-box testing.
NewCryptoProvider = newTestCryptoProvider
// newTestInspector allows instantiating the private inspector for
// black-box testing.
// NewCSRouter allows instantiating the private CS router for black-box
// testing.
NewCSRouter = newTestCSRouter
// NewFwdInserter allows instantiating the private forwarding
// inserter for black-box testing.
NewFwdInserter = newTestFwdInserter
// NewInserter allows instantiating the private inserter for black-box
// testing.
NewInserter = newTestInserter
// NewTestInspector allows instantiating the private inspector for black-box
// testing.
NewTestInspector = newTestInspector
// NewLocalRouter allows instantiating the private resolver for black-box
// testing.
NewLocalRouter = newTestLocalRouter
// NewResolver allows instantiating the private resolver for black-box
// testing.
NewResolver = newTestResolver
Expand All @@ -39,13 +56,47 @@ func newTestCryptoProvider(db DBRead, recurser Recurser, resolver Resolver, rout
}
}

// newTestCSRouter returns a new router for testing.
func newTestCSRouter(isd addr.ISD, router snet.Router, db TRCRead) Router {
return &csRouter{
isd: isd,
router: router,
db: db,
}
}

// newTestFwdInserter returns a new forwarding inserter for testing.
func newTestFwdInserter(db ReadWrite, rpc RPC) Inserter {
return &fwdInserter{
baseInserter: baseInserter{
db: db,
},
rpc: rpc,
}
}

// newTestInserter returns a new inserter for testing.
func newTestInserter(db ReadWrite, unsafe bool) Inserter {
return &inserter{
baseInserter: baseInserter{
db: db,
unsafe: unsafe,
},
}
}

// newTestInspector returns a new inspector for testing.
func newTestInspector(provider CryptoProvider) Inspector {
return &inspector{
provider: provider,
}
}

// newTestLocalRouter returns a new router for testing.
func newTestLocalRouter(ia addr.IA) Router {
return &localRouter{ia: ia}
}

// newTestResolver returns a new resolver for testing.
func newTestResolver(db DBRead, inserter Inserter, rpc RPC) Resolver {
return &resolver{
Expand Down
158 changes: 153 additions & 5 deletions go/lib/infra/modules/trust/v2/inserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,180 @@ package trust

import (
"context"
"errors"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/internal/decoded"
"github.com/scionproto/scion/go/lib/scrypto"
"github.com/scionproto/scion/go/lib/scrypto/trc/v2"
"github.com/scionproto/scion/go/lib/serrors"
)

var (
// ErrBaseNotSupported indicates base TRC insertion is not supported.
ErrBaseNotSupported = serrors.New("inserting base TRC not supported")
// ErrValidation indicates a validation error.
ErrValidation = errors.New("validation error")
ErrValidation = serrors.New("validation error")
// ErrVerification indicates a verification error.
ErrVerification = errors.New("verification error")
ErrVerification = serrors.New("verification error")
)

// Inserter inserts and verifies trust material into the database.
type Inserter interface {
// InsertTRC verifies the signed TRC and inserts it into the database.
// The previous TRC is queried through the provider function, when necessary.
InsertTRC(ctx context.Context, decoded decoded.TRC, trcProvider TRCProviderFunc) error
InsertTRC(ctx context.Context, decTRC decoded.TRC, trcProvider TRCProviderFunc) error
// InsertChain verifies the signed certificate chain and inserts it into the
// database. The issuing TRC is queried through the provider function, when
// necessary.
InsertChain(ctx context.Context, decoded decoded.Chain, trcProvider TRCProviderFunc) error
InsertChain(ctx context.Context, decChain decoded.Chain, trcProvider TRCProviderFunc) error
}

// TRCProviderFunc provides TRCs. It is used to configure the TRC retrieval
// method of the inserter.
type TRCProviderFunc func(context.Context, addr.ISD, scrypto.Version) (*trc.TRC, error)

// inserter is used to verify and insert trust material into the database.
type inserter struct {
baseInserter
}

// InsertTRC verifies the signed TRC and inserts it into the database.
// The previous TRC is queried through the provider function, when necessary.
func (ins *inserter) InsertTRC(ctx context.Context, decTRC decoded.TRC,
trcProvider TRCProviderFunc) error {

if insert, err := ins.shouldInsertTRC(ctx, decTRC, trcProvider); err != nil || !insert {
return err
}
if _, err := ins.db.InsertTRC(ctx, decTRC); err != nil {
return serrors.WrapStr("unable to insert TRC", err)
}
return nil
}

// InsertChain verifies the signed certificate chain and inserts it into the
// database. The issuing TRC is queried through the provider function, when
// necessary.
func (ins *inserter) InsertChain(ctx context.Context, chain decoded.Chain,
trcProvider TRCProviderFunc) error {

if insert, err := ins.shouldInsertChain(ctx, chain, trcProvider); err != nil || !insert {
return err
}
if _, _, err := ins.db.InsertChain(ctx, chain); err != nil {
return serrors.WrapStr("unable to insert chain", err)
}
return nil
}

// fwdInserter is an inserter that always forwards the trust material to the
// certificate server before inserting it into the database. Forwarding must be
// successful, otherwise the material is not inserted into the database.
type fwdInserter struct {
baseInserter
router localRouter
rpc RPC
}

// InsertTRC verifies the signed TRC and inserts it into the database. The
// previous TRC is queried through the provider function, when necessary. Before
// insertion, the TRC is forwarded to the certificate server. If the certificate
// server does not successfully handle the TRC, the insertion fails.
func (ins *fwdInserter) InsertTRC(ctx context.Context, decTRC decoded.TRC,
trcProvider TRCProviderFunc) error {

if insert, err := ins.shouldInsertTRC(ctx, decTRC, trcProvider); err != nil || !insert {
return err
}
cs := ins.router.chooseServer()
if err := ins.rpc.SendTRC(ctx, decTRC.Raw, cs); err != nil {
return serrors.WrapStr("unable to push TRC to certificate server", err, "addr", cs)
}
if _, err := ins.db.InsertTRC(ctx, decTRC); err != nil {
return serrors.WrapStr("unable to insert TRC", err)
}
return nil
}

// InsertChain verifies the signed certificate chain and inserts it into the
// database. The issuing TRC is queried through the provider function, when
// necessary. Before insertion, the certificate chain is forwarded to the
// certificate server. If the certificate server does not successfully handle
// the certificate chain, the insertion fails.
func (ins *fwdInserter) InsertChain(ctx context.Context, chain decoded.Chain,
trcProvider TRCProviderFunc) error {

if insert, err := ins.shouldInsertChain(ctx, chain, trcProvider); err != nil || !insert {
return err
}
cs := ins.router.chooseServer()
if err := ins.rpc.SendCertChain(ctx, chain.Raw, cs); err != nil {
return serrors.WrapStr("unable to push chain to certificate server", err,
"addr", cs)
}
if _, _, err := ins.db.InsertChain(ctx, chain); err != nil {
return serrors.WrapStr("unable to insert chain", err)
}
return nil
}

type baseInserter struct {
db ReadWrite
// unsafe allows inserts of base TRCs. This is used as a workaround until
// TAAC support is implemented.
unsafe bool
}

func (ins *baseInserter) shouldInsertTRC(ctx context.Context, decTRC decoded.TRC,
trcProvider TRCProviderFunc) (bool, error) {

found, err := ins.db.TRCExists(ctx, decTRC)
if err != nil || found {
return !found, err
}
if decTRC.TRC.Base() {
// XXX(roosd): remove when TAACs are supported.
if ins.unsafe {
if _, err := ins.db.InsertTRC(ctx, decTRC); err != nil {
return false, serrors.WrapStr("unable to insert base TRC", err)
}
return false, nil
}
return false, serrors.WithCtx(ErrBaseNotSupported, "trc", decTRC)
}
prev, err := trcProvider(ctx, decTRC.TRC.ISD, decTRC.TRC.Version-1)
if err != nil {
return false, serrors.WrapStr("unable to get previous TRC", err,
"isd", decTRC.TRC.ISD, "version", decTRC.TRC.Version-1)
}
if err := ins.checkUpdate(ctx, prev, decTRC); err != nil {
return false, serrors.WrapStr("error checking TRC update", err)
}
return true, nil
}

func (ins *baseInserter) checkUpdate(ctx context.Context, prev *trc.TRC, next decoded.TRC) error {
validator := trc.UpdateValidator{
Next: next.TRC,
Prev: prev,
}
if _, err := validator.Validate(); err != nil {
return serrors.Wrap(ErrValidation, err)
}
verifier := trc.UpdateVerifier{
Next: next.TRC,
NextEncoded: next.Signed.EncodedTRC,
Prev: prev,
Signatures: next.Signed.Signatures,
}
if err := verifier.Verify(); err != nil {
return serrors.Wrap(ErrVerification, err)
}
return nil
}

func (ins *baseInserter) shouldInsertChain(ctx context.Context, chain decoded.Chain,
trcProvider TRCProviderFunc) (bool, error) {

return false, serrors.New("not implemented")
}
100 changes: 100 additions & 0 deletions go/lib/infra/modules/trust/v2/inserter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2019 Anapaya Systems
//
// 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 trust_test

import (
"context"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"

"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/internal/decoded"
"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/mock_v2"
)

func TestInserterInsertTRC(t *testing.T) {
tests := map[string]struct {
Expect func(*mock_v2.MockDB, decoded.TRC)
Unsafe bool
ExpectedErr error
}{
"Exists with same contents": {
Expect: func(db *mock_v2.MockDB, decTRC decoded.TRC) {
db.EXPECT().TRCExists(gomock.Any(), decTRC).Return(
true, nil,
)
},
},
"Exists with different contents": {
Expect: func(db *mock_v2.MockDB, decTRC decoded.TRC) {
db.EXPECT().TRCExists(gomock.Any(), decTRC).Return(
true, trust.ErrContentMismatch,
)
},
ExpectedErr: trust.ErrContentMismatch,
},
"Base TRC and unsafe set": {
Expect: func(db *mock_v2.MockDB, decTRC decoded.TRC) {
db.EXPECT().TRCExists(gomock.Any(), decTRC).Return(
false, nil,
)
db.EXPECT().InsertTRC(gomock.Any(), decTRC).Return(true, nil)
},
Unsafe: true,
},
"Base TRC and unsafe set, insert fail": {
Expect: func(db *mock_v2.MockDB, decTRC decoded.TRC) {
db.EXPECT().TRCExists(gomock.Any(), decTRC).Return(
false, nil,
)
db.EXPECT().InsertTRC(gomock.Any(), decTRC).Return(
false, trust.ErrContentMismatch,
)
},
ExpectedErr: trust.ErrContentMismatch,
Unsafe: true,
},
"Base TRC and unsafe not set": {
Expect: func(db *mock_v2.MockDB, decTRC decoded.TRC) {
db.EXPECT().TRCExists(gomock.Any(), decTRC).Return(
false, nil,
)
},
ExpectedErr: trust.ErrBaseNotSupported,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
mctrl := gomock.NewController(t)
defer mctrl.Finish()

db := mock_v2.NewMockDB(mctrl)
decoded := loadTRC(t, trc1v1)
test.Expect(db, decoded)
ins := trust.NewInserter(db, test.Unsafe)

err := ins.InsertTRC(context.Background(), decoded, nil)
if test.ExpectedErr != nil {
require.Truef(t, xerrors.Is(err, test.ExpectedErr),
"Expected: %s Actual: %s", test.ExpectedErr, err)
} else {
require.NoError(t, err)
}
})
}
}
Loading