Skip to content

Commit

Permalink
TrustStore: Implement inserter (#3225)
Browse files Browse the repository at this point in the history
Adds:
- Implement TRC verification and insertion logic.
- The forwarding inserter registers the new trust material with the
  local certificate server before inserting into the database.
  It is supposed to be used by the beacon and path server.
  • Loading branch information
oncilla authored Oct 7, 2019
1 parent 46c1c65 commit 06e9df0
Show file tree
Hide file tree
Showing 6 changed files with 530 additions and 7 deletions.
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

0 comments on commit 06e9df0

Please sign in to comment.