Repository: scionproto/scion
Pull request #3538 (2019-12-17 11:43:53)

TrustStore: Combine components to Store
This PR combines the individual components to the trust store and enables it in the topology.
Additionally, legacy reissuance handler and tasks are removed,

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3538)
<!-- Reviewable:end -->



Patch:
diff --git a/acceptance/cert_renewal_acceptance/test b/acceptance/cert_renewal_acceptance_disabled/test
similarity index 100%
rename from acceptance/cert_renewal_acceptance/test
rename to acceptance/cert_renewal_acceptance_disabled/test
diff --git a/go/beacon_srv/BUILD.bazel b/go/beacon_srv/BUILD.bazel
index 76d17bc55..56831967f 100644
--- a/go/beacon_srv/BUILD.bazel
+++ b/go/beacon_srv/BUILD.bazel
@@ -25,8 +25,7 @@ go_library(
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
+        "//go/lib/infra/modules/trust/v2:go_default_library",
         "//go/lib/keyconf:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/periodic:go_default_library",
diff --git a/go/beacon_srv/main.go b/go/beacon_srv/main.go
index d0b401da0..182f7edc6 100644
--- a/go/beacon_srv/main.go
+++ b/go/beacon_srv/main.go
@@ -49,8 +49,7 @@ import (
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
 	"github.com/scionproto/scion/go/lib/keyconf"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/periodic"
@@ -101,25 +100,8 @@ func realMain() int {
 		log.Crit("Setup failed", "err", err)
 		return 1
 	}
-	trustDB, err := cfg.TrustDB.New()
-	if err != nil {
-		log.Crit("Unable to initialize trustDB", "err", err)
-		return 1
-	}
-	trustDB = trustdb.WithMetrics(string(cfg.TrustDB.Backend()), trustDB)
-	defer trustDB.Close()
+
 	topo := itopo.Get()
-	trustConf := trust.Config{
-		MustHaveLocalChain: true,
-		ServiceType:        proto.ServiceType_bs,
-		TopoProvider:       itopo.Provider(),
-	}
-	trustStore := trust.NewStore(trustDB, topo.IA(), trustConf, log.Root())
-	err = trustStore.LoadAuthoritativeCrypto(filepath.Join(cfg.General.ConfigDir, "certs"))
-	if err != nil {
-		log.Crit("Unable to load local crypto", "err", err)
-		return 1
-	}
 	if !topo.Exists(addr.SvcBS, cfg.General.ID) {
 		log.Crit("Unable to find topo address")
 		return 1
@@ -143,7 +125,6 @@ func realMain() int {
 			KeyFile:  cfg.QUIC.KeyFile,
 		},
 		SVCResolutionFraction: cfg.QUIC.ResolutionFraction,
-		TrustStore:            trustStore,
 		SVCRouter:             messenger.NewSVCRouter(itopo.Provider()),
 	}
 	msgr, err := nc.Messenger()
@@ -152,6 +133,40 @@ func realMain() int {
 		return 1
 	}
 	defer msgr.CloseServer()
+
+	trustDB, err := cfg.TrustDB.New()
+	if err != nil {
+		log.Crit("Error initializing trust database", "err", err)
+		return 1
+	}
+	defer trustDB.Close()
+	inserter := trust.ForwardingInserter{
+		BaseInserter: trust.BaseInserter{DB: trustDB},
+		Router:       trust.LocalRouter{IA: topo.IA()},
+		RPC:          trust.DefaultRPC{Msgr: msgr},
+	}
+	provider := trust.Provider{
+		DB:       trustDB,
+		Recurser: trust.LocalOnlyRecurser{},
+		Resolver: trust.DefaultResolver{
+			DB:       trustDB,
+			Inserter: inserter,
+			RPC:      trust.DefaultRPC{Msgr: msgr},
+		},
+		Router: trust.LocalRouter{IA: topo.IA()},
+	}
+	trustStore := trust.Store{
+		Inspector:      trust.DefaultInspector{Provider: provider},
+		CryptoProvider: provider,
+		Inserter:       inserter,
+		DB:             trustDB,
+	}
+	certsDir := filepath.Join(cfg.General.ConfigDir, "certs")
+	if err = trustStore.LoadCryptoMaterial(context.Background(), certsDir); err != nil {
+		log.Crit("Error loading crypto material", "err", err)
+		return 1
+	}
+
 	store, err := loadStore(topo.Core(), topo.IA(), cfg)
 	if err != nil {
 		log.Crit("Unable to open beacon store", "err", err)
@@ -160,13 +175,13 @@ func realMain() int {
 	defer store.Close()
 	intfs = ifstate.NewInterfaces(topo.IFInfoMap(), ifstate.Config{})
 	prometheus.MustRegister(ifstate.NewCollector(intfs))
-	msgr.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler(false))
-	msgr.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler(false))
+	msgr.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler())
+	msgr.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler())
 	msgr.AddHandler(infra.IfStateReq, ifstate.NewHandler(intfs))
 	msgr.AddHandler(infra.SignedRev, revocation.NewHandler(store,
-		trustStore.NewVerifier(), 5*time.Second))
+		trust.NewVerifier(trustStore), 5*time.Second))
 	msgr.AddHandler(infra.Seg, beaconing.NewHandler(topo.IA(), intfs, store,
-		trustStore.NewVerifier()))
+		trust.NewVerifier(trustStore)))
 	msgr.AddHandler(infra.IfId, keepalive.NewHandler(topo.IA(), intfs,
 		keepalive.StateChangeTasks{
 			RevDropper: store,
@@ -205,7 +220,7 @@ func realMain() int {
 	tasks = &periodicTasks{
 		intfs:        intfs,
 		conn:         conn.(*snet.SCIONPacketConn),
-		trustDB:      trustDB,
+		trustStore:   trustStore,
 		store:        store,
 		msgr:         msgr,
 		topoProvider: itopo.Provider(),
@@ -266,7 +281,7 @@ type periodicTasks struct {
 	intfs           *ifstate.Interfaces
 	conn            *snet.SCIONPacketConn
 	genMac          func() hash.Hash
-	trustDB         trustdb.TrustDB
+	trustStore      trust.Store
 	store           beaconstorage.Store
 	msgr            infra.Messenger
 	topoProvider    topology.Provider
@@ -485,22 +500,17 @@ func (t *periodicTasks) startRegistrar(topo topology.Topology, segType proto.Pat
 }
 
 func (t *periodicTasks) createSigner(ia addr.IA) (infra.Signer, error) {
-	dir := filepath.Join(cfg.General.ConfigDir, "keys")
-	cfg, err := keyconf.Load(dir, false, false, false, false)
-	if err != nil {
-		return nil, common.NewBasicError("unable to load key config", err)
-	}
 	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
 	defer cancelF()
-	meta, err := trust.CreateSignMeta(ctx, ia, t.trustDB)
-	if err != nil {
-		return nil, common.NewBasicError("unable to create sign meta", err)
-	}
-	signer, err := trust.NewBasicSigner(cfg.SignKey, meta)
-	if err != nil {
-		return nil, common.NewBasicError("unable to create signer", err)
+	gen := trust.SignerGen{
+		IA:       itopo.Get().IA(),
+		Provider: t.trustStore,
+		KeyRing: keyconf.LoadingRing{
+			Dir: filepath.Join(cfg.General.ConfigDir, "keys"),
+			IA:  ia,
+		},
 	}
-	return signer, nil
+	return gen.Signer(ctx)
 }
 
 func (t *periodicTasks) Kill() {
diff --git a/go/cert_srv/BUILD.bazel b/go/cert_srv/BUILD.bazel
index 4f6ce379a..3df37bdd2 100644
--- a/go/cert_srv/BUILD.bazel
+++ b/go/cert_srv/BUILD.bazel
@@ -8,7 +8,6 @@ go_library(
     visibility = ["//visibility:private"],
     deps = [
         "//go/cert_srv/internal/config:go_default_library",
-        "//go/cert_srv/internal/reiss:go_default_library",
         "//go/lib/addr:go_default_library",
         "//go/lib/common:go_default_library",
         "//go/lib/discovery:go_default_library",
@@ -19,8 +18,8 @@ go_library(
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
+        "//go/lib/infra/modules/trust/v2:go_default_library",
+        "//go/lib/keyconf:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/periodic:go_default_library",
         "//go/lib/prom:go_default_library",
diff --git a/go/cert_srv/internal/config/BUILD.bazel b/go/cert_srv/internal/config/BUILD.bazel
index 50805c567..3818ff97e 100644
--- a/go/cert_srv/internal/config/BUILD.bazel
+++ b/go/cert_srv/internal/config/BUILD.bazel
@@ -5,7 +5,6 @@ go_library(
     srcs = [
         "config.go",
         "sample.go",
-        "state.go",
     ],
     importpath = "github.com/scionproto/scion/go/cert_srv/internal/config",
     visibility = ["//go/cert_srv:__subpackages__"],
@@ -13,11 +12,7 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/config:go_default_library",
         "//go/lib/env:go_default_library",
-        "//go/lib/infra:go_default_library",
         "//go/lib/infra/modules/idiscovery:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
-        "//go/lib/keyconf:go_default_library",
         "//go/lib/scrypto/cert:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/truststorage:go_default_library",
@@ -27,20 +22,14 @@ go_library(
 
 go_test(
     name = "go_default_test",
-    srcs = [
-        "config_test.go",
-        "state_test.go",
-    ],
+    srcs = ["config_test.go"],
     data = glob(["testdata/**"]),
     embed = [":go_default_library"],
     deps = [
         "//go/lib/env/envtest:go_default_library",
         "//go/lib/infra/modules/idiscovery/idiscoverytest:go_default_library",
-        "//go/lib/keyconf:go_default_library",
-        "//go/lib/scrypto:go_default_library",
         "//go/lib/truststorage/truststoragetest:go_default_library",
         "@com_github_burntsushi_toml//:go_default_library",
-        "@com_github_smartystreets_goconvey//convey:go_default_library",
         "@com_github_stretchr_testify//assert:go_default_library",
     ],
 )
diff --git a/go/cert_srv/internal/config/state.go b/go/cert_srv/internal/config/state.go
deleted file mode 100644
index 765cecce2..000000000
--- a/go/cert_srv/internal/config/state.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2018 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 config
-
-import (
-	"path/filepath"
-	"sync"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
-	"github.com/scionproto/scion/go/lib/keyconf"
-)
-
-type State struct {
-	// Store is the trust store.
-	Store *trust.Store
-	// TrustDB is the trust DB.
-	TrustDB trustdb.TrustDB
-	// keyConf contains the AS level keys.
-	keyConf *keyconf.Conf
-	// keyConfLock guards KeyConf.
-	keyConfLock sync.RWMutex
-	// signer is used to sign ctrl payloads.
-	signer infra.Signer
-	// signerLock guards signer.
-	signerLock sync.RWMutex
-	// verifier is used to verify ctrl payloads.
-	verifier infra.Verifier
-	// verifierLock guards verifier.
-	verifierLock sync.RWMutex
-}
-
-func LoadState(confDir string, isCore bool, trustDB trustdb.TrustDB,
-	trustStore *trust.Store) (*State, error) {
-
-	s := &State{
-		Store:   trustStore,
-		TrustDB: trustDB,
-	}
-	if err := s.loadKeyConf(confDir, isCore); err != nil {
-		return nil, err
-	}
-	return s, nil
-}
-
-// loadKeyConf loads the key configuration.
-func (s *State) loadKeyConf(confDir string, isCore bool) error {
-	var err error
-	s.keyConf, err = keyconf.Load(filepath.Join(confDir, "keys"), isCore, isCore, false, true)
-	if err != nil {
-		return common.NewBasicError(ErrKeyConf, err)
-	}
-	return nil
-}
-
-// GetSigningKey returns the signing key of the current key configuration.
-func (s *State) GetSigningKey() common.RawBytes {
-	s.keyConfLock.RLock()
-	defer s.keyConfLock.RUnlock()
-	return s.keyConf.SignKey
-}
-
-// GetIssSigningKey returns the issuer signing key of the current key configuration.
-func (s *State) GetIssSigningKey() common.RawBytes {
-	s.keyConfLock.RLock()
-	defer s.keyConfLock.RUnlock()
-	return s.keyConf.IssSigKey
-}
-
-// GetDecryptKey returns the decryption key of the current key configuration.
-func (s *State) GetDecryptKey() common.RawBytes {
-	s.keyConfLock.RLock()
-	defer s.keyConfLock.RUnlock()
-	return s.keyConf.DecryptKey
-}
-
-// GetOnRootKey returns the online root key of the current key configuration.
-func (s *State) GetOnRootKey() common.RawBytes {
-	s.keyConfLock.RLock()
-	defer s.keyConfLock.RUnlock()
-	return s.keyConf.OnRootKey
-}
-
-// GetSigner returns the signer of the current configuration.
-func (s *State) GetSigner() infra.Signer {
-	s.signerLock.RLock()
-	defer s.signerLock.RUnlock()
-	return s.signer
-}
-
-// SetSigner sets the signer of the current configuration.
-func (s *State) SetSigner(signer infra.Signer) {
-	s.signerLock.Lock()
-	defer s.signerLock.Unlock()
-	s.signer = signer
-}
-
-// GetVerifier returns the verifier of the current configuration.
-func (s *State) GetVerifier() infra.Verifier {
-	s.verifierLock.RLock()
-	defer s.verifierLock.RUnlock()
-	return s.verifier
-}
-
-// SetVerifier sets the verifier of the current configuration.
-func (s *State) SetVerifier(verifier infra.Verifier) {
-	s.verifierLock.Lock()
-	defer s.verifierLock.Unlock()
-	s.verifier = verifier
-}
diff --git a/go/cert_srv/internal/reiss/BUILD.bazel b/go/cert_srv/internal/reiss/BUILD.bazel
deleted file mode 100644
index e67e41301..000000000
--- a/go/cert_srv/internal/reiss/BUILD.bazel
+++ /dev/null
@@ -1,53 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "corepush.go",
-        "handler.go",
-        "requester.go",
-        "self.go",
-    ],
-    importpath = "github.com/scionproto/scion/go/cert_srv/internal/reiss",
-    visibility = ["//go/cert_srv:__subpackages__"],
-    deps = [
-        "//go/cert_srv/internal/config:go_default_library",
-        "//go/lib/addr:go_default_library",
-        "//go/lib/common:go_default_library",
-        "//go/lib/ctrl:go_default_library",
-        "//go/lib/ctrl/cert_mgmt:go_default_library",
-        "//go/lib/infra:go_default_library",
-        "//go/lib/infra/messenger:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
-        "//go/lib/log:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/scrypto:go_default_library",
-        "//go/lib/scrypto/cert:go_default_library",
-        "//go/lib/scrypto/trc:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/snet:go_default_library",
-        "//go/lib/util:go_default_library",
-        "@org_golang_x_crypto//ed25519:go_default_library",
-    ],
-)
-
-go_test(
-    name = "go_default_test",
-    srcs = ["corepush_test.go"],
-    data = glob(["testdata/**"]),
-    embed = [":go_default_library"],
-    deps = [
-        "//go/lib/common:go_default_library",
-        "//go/lib/ctrl/cert_mgmt:go_default_library",
-        "//go/lib/infra/mock_infra:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb/mock_trustdb:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/scrypto/cert:go_default_library",
-        "//go/lib/scrypto/trc:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/lib/xtest:go_default_library",
-        "//go/lib/xtest/matchers:go_default_library",
-        "@com_github_golang_mock//gomock:go_default_library",
-    ],
-)
diff --git a/go/cert_srv/internal/reiss/corepush.go b/go/cert_srv/internal/reiss/corepush.go
deleted file mode 100644
index 0f7422176..000000000
--- a/go/cert_srv/internal/reiss/corepush.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// 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 reiss
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/cert_mgmt"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/snet"
-)
-
-var (
-	// SleepAfterFailure is the base time to sleep after a failed attempt to push the chain.
-	// The actual sleep time is: attempts * SleepAfterFailure.
-	SleepAfterFailure = time.Second
-	// DefaultTryTimeout is the default timeout for one sync try if the context
-	// has no deadline set.
-	DefaultTryTimeout = 20 * time.Second
-)
-
-var _ periodic.Task = (*CorePusher)(nil)
-
-// CorePusher is a periodic.Task that pushes the local chain to all core CSes in the ISD.
-// The interval this task is run in is expected to be rather large (e.g. 1h).
-type CorePusher struct {
-	LocalIA addr.IA
-	TrustDB trustdb.TrustDB
-	Msger   infra.Messenger
-}
-
-// Name returns the tasks name.
-func (p *CorePusher) Name() string {
-	return "cs_reiss_corepusher"
-}
-
-// Run makes sure all core CS have the chain of the local AS.
-func (p *CorePusher) Run(ctx context.Context) {
-	logger := log.FromCtx(ctx)
-	chain, err := p.TrustDB.GetChainMaxVersion(ctx, p.LocalIA)
-	if err != nil {
-		logger.Error("[reiss.CorePusher] Failed to get local chain from DB", "err", err)
-		return
-	}
-	cores, err := p.coreASes(ctx)
-	if err != nil {
-		logger.Error("[reiss.CorePusher] Failed to determine core ASes", "err", err)
-		return
-	}
-	numCores := len(cores.ias)
-	tryTimeout := DefaultTryTimeout
-	if deadline, ok := ctx.Deadline(); ok {
-		tryTimeout = deadline.Sub(time.Now()) / 3
-	}
-	for syncTries := 0; syncTries < 3 && ctx.Err() == nil; syncTries++ {
-		tryCtx, cancelF := context.WithTimeout(ctx, tryTimeout)
-		err = p.syncCores(tryCtx, chain, cores)
-		cancelF()
-		if err == nil {
-			logger.Info("[reiss.CorePusher] Successfully pushed chain to cores", "cores", numCores)
-			return
-		}
-		logger.Error("[reiss.CorePusher] Failed to sync all cores", "err", err)
-		select {
-		case <-time.After(time.Duration(syncTries+1) * SleepAfterFailure):
-		case <-ctx.Done():
-		}
-	}
-}
-
-func (p *CorePusher) coreASes(ctx context.Context) (*iaMap, error) {
-	trc, err := p.TrustDB.GetTRCMaxVersion(ctx, p.LocalIA.I)
-	if err != nil {
-		return nil, common.NewBasicError("Unable to get TRC for local ISD", err)
-	}
-	cores := make(map[addr.IA]struct{})
-	for _, ia := range trc.CoreASes.ASList() {
-		if !p.LocalIA.Equal(ia) {
-			cores[ia] = struct{}{}
-		}
-	}
-	return &iaMap{ias: cores}, nil
-}
-
-// syncCores tries to sync to the given cores and returns the cores for which the syncing failed.
-func (p *CorePusher) syncCores(ctx context.Context, chain *cert.Chain, cores *iaMap) error {
-
-	wg := &sync.WaitGroup{}
-	wg.Add(len(cores.ias))
-	checkErrs := &iaList{}
-	for coreIA := range cores.ias {
-		p.asyncPush(ctx, chain, cores, coreIA, checkErrs, wg)
-	}
-	wg.Wait()
-	if len(checkErrs.ias) > 0 || len(cores.ias) > 0 {
-		return common.NewBasicError("Sync error", nil, "checkErrors", checkErrs.ias,
-			"remainingCores", cores.list())
-	}
-	return nil
-}
-
-// asyncPush pushes the certificate chain to the core if it does not have it already.
-func (p *CorePusher) asyncPush(ctx context.Context, chain *cert.Chain, cores *iaMap,
-	core addr.IA, checkErrs *iaList, wg *sync.WaitGroup) {
-
-	go func() {
-		defer log.LogPanicAndExit()
-		defer wg.Done()
-		hasChain, err := p.hasChain(ctx, core, chain)
-		if err != nil {
-			checkErrs.append(core)
-			// fall-through explicitly, we just assume the core doesn't have it and send it.
-		}
-		var sendErr error
-		if !hasChain {
-			sendErr = p.sendChain(ctx, core, chain)
-		}
-		if sendErr == nil {
-			cores.delete(core)
-		}
-	}()
-}
-
-func (p *CorePusher) hasChain(ctx context.Context, coreAS addr.IA,
-	expectedChain *cert.Chain) (bool, error) {
-
-	chainIA, ver := expectedChain.IAVer()
-	req := &cert_mgmt.ChainReq{
-		RawIA:     chainIA.IAInt(),
-		Version:   ver,
-		CacheOnly: true,
-	}
-	a := snet.NewSVCAddr(coreAS, nil, nil, addr.SvcCS)
-	reply, err := p.Msger.GetCertChain(ctx, req, a, messenger.NextId())
-	if err != nil {
-		return false, common.NewBasicError("Error during fetch", err)
-	}
-	chain, err := reply.Chain()
-	return chain != nil, err
-}
-
-func (p *CorePusher) sendChain(ctx context.Context, coreAS addr.IA, chain *cert.Chain) error {
-	rawChain, err := chain.Compress()
-	if err != nil {
-		return common.NewBasicError("Failed to compress chain", err)
-	}
-	msg := &cert_mgmt.Chain{
-		RawChain: rawChain,
-	}
-	a := snet.NewSVCAddr(coreAS, nil, nil, addr.SvcCS)
-	// TODO(lukedirtwalker): Expect Acks.
-	return p.Msger.SendCertChain(ctx, msg, a, messenger.NextId())
-}
-
-type iaList struct {
-	mu  sync.Mutex
-	ias []addr.IA
-}
-
-func (l *iaList) append(ia addr.IA) {
-	l.mu.Lock()
-	l.ias = append(l.ias, ia)
-	l.mu.Unlock()
-}
-
-type iaMap struct {
-	mu  sync.Mutex
-	ias map[addr.IA]struct{}
-}
-
-func (m *iaMap) delete(ia addr.IA) {
-	m.mu.Lock()
-	delete(m.ias, ia)
-	m.mu.Unlock()
-}
-
-func (m *iaMap) list() []addr.IA {
-	m.mu.Lock()
-	l := make([]addr.IA, 0, len(m.ias))
-	for ia := range m.ias {
-		l = append(l, ia)
-	}
-	m.mu.Unlock()
-	return l
-}
diff --git a/go/cert_srv/internal/reiss/handler.go b/go/cert_srv/internal/reiss/handler.go
deleted file mode 100644
index be3e5fef7..000000000
--- a/go/cert_srv/internal/reiss/handler.go
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2018 ETH Zurich, 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 reiss
-
-import (
-	"bytes"
-	"context"
-	"net"
-	"time"
-
-	"github.com/scionproto/scion/go/cert_srv/internal/config"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl"
-	"github.com/scionproto/scion/go/lib/ctrl/cert_mgmt"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/snet"
-	"github.com/scionproto/scion/go/lib/util"
-)
-
-const (
-	HandlerTimeout = 5 * time.Second
-)
-
-// Errors
-const (
-	ErrKeyChanged   common.ErrMsg = "Verifying key has changed in the meantime"
-	ErrNotACustomer common.ErrMsg = "ISD-AS not in customer mapping"
-)
-
-// Handler handles certificate chain reissue requests.
-//
-// Reissue requests are sent by non-issuer ASes to issuer ASes. The request
-// needs to be signed with the private key associated with the newest verifying
-// key in the customer mapping. Certificate chains are issued automatically by
-// the issuer ASes.
-type Handler struct {
-	State *config.State
-	IA    addr.IA
-}
-
-func (h *Handler) Handle(r *infra.Request) *infra.HandlerResult {
-	var addr *snet.Addr
-	switch peer := r.Peer.(type) {
-	case *snet.Addr:
-		addr = peer
-	case *snet.UDPAddr:
-		addr = peer.ToAddr()
-	case *snet.SVCAddr:
-		addr = peer.ToAddr()
-	}
-
-	req := r.Message.(*cert_mgmt.ChainIssReq)
-	if err := h.handle(r, addr, req); err != nil {
-		log.Error("[reiss.Handler] Dropping certificate reissue request",
-			"addr", addr, "req", req, "err", err)
-	}
-	// TODO(lukedirtwalker): reflect error in metrics.
-	return infra.MetricsResultOk
-}
-
-// handle handles certificate chain reissue requests. If the requested
-// certificate chain is already present, the existing certificate chain is
-// resent. Otherwise, a new certificate chain is issued.
-func (h *Handler) handle(r *infra.Request, addr *snet.Addr, req *cert_mgmt.ChainIssReq) error {
-	ctx, cancelF := context.WithTimeout(r.Context(), HandlerTimeout)
-	defer cancelF()
-	logger := log.FromCtx(ctx)
-	signed := r.FullMessage.(*ctrl.SignedPld)
-	logger.Trace("[reiss.Handler] Received certificate reissue request", "addr", addr, "req", req)
-	// Validate the request was correctly signed by the requester
-	verChain, err := h.validateSign(ctx, addr, signed)
-	if err != nil {
-		return common.NewBasicError("Unable to validate chain", err)
-	}
-	// Parse the requested certificate
-	crt, err := req.Cert()
-	if err != nil {
-		return common.NewBasicError("Unable to parse requested certificate", err)
-	}
-	// Respond with max chain for outdated requests.
-	opts := infra.ChainOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	maxChain, err := h.State.Store.GetChain(ctx, verChain.Leaf.Subject, scrypto.LatestVer, opts)
-	if err != nil {
-		return common.NewBasicError("Unable to fetch max chain", err)
-	}
-	if maxChain != nil && crt.Version <= maxChain.Leaf.Version {
-		logger.Info("[reiss.Handler] Resending certificate chain", "addr", addr, "req", req)
-		return h.sendRep(ctx, addr, maxChain)
-	}
-	// Get the verifying key from the customer mapping
-	verKey, verVersion, err := h.getVerifyingKey(ctx, addr.IA)
-	if err != nil {
-		return common.NewBasicError("Unable to get verifying key", err)
-	}
-	// Verify request and check the verifying key matches
-	if err = h.validateReq(crt, verKey, verChain, maxChain); err != nil {
-		return common.NewBasicError("Unable to verify request", err)
-	}
-	// Issue certificate chain
-	newChain, err := h.issueChain(ctx, crt, verKey, verVersion)
-	if err != nil {
-		return common.NewBasicError("Unable to reissue certificate chain", err)
-	}
-	// Send issued certificate chain
-	if err := h.sendRep(ctx, addr, newChain); err != nil {
-		return common.NewBasicError("Unable to send reissued certificate chain", err)
-	}
-	return nil
-}
-
-// validateSign validates that the signer matches the requester and returns the
-// certificate chain used when verifying the signature.
-func (h *Handler) validateSign(ctx context.Context, addr *snet.Addr,
-	signed *ctrl.SignedPld) (*cert.Chain, error) {
-
-	if signed.Sign == nil {
-		return nil, serrors.New("Sign is nil")
-	}
-	src, err := ctrl.NewSignSrcDefFromRaw(signed.Sign.Src)
-	if err != nil {
-		return nil, err
-	}
-	opts := infra.ChainOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	verChain, err := h.State.Store.GetChain(ctx, src.IA, scrypto.Version(src.ChainVer), opts)
-	if err != nil {
-		return nil, err
-	}
-	if signed.Sign.Type.String() != verChain.Leaf.SignAlgorithm {
-		return nil, common.NewBasicError("Invalid sign type", nil,
-			"expected", verChain.Leaf.SignAlgorithm, "actual", signed.Sign.Type)
-	}
-	// Verify that the requester matches the signer
-	if !verChain.Leaf.Subject.Equal(addr.IA) {
-		return nil, common.NewBasicError("Origin AS does not match signer", nil,
-			"signer", verChain.Leaf.Subject, "origin", addr.IA)
-	}
-	return verChain, nil
-}
-
-// validateReq validates the requested certificate. Additionally, it validates that
-// the request was verified with the same verifying key as in the customer mapping.
-func (h *Handler) validateReq(c *cert.Certificate, vKey common.RawBytes,
-	vChain, maxChain *cert.Chain) error {
-
-	if !c.Subject.Equal(vChain.Leaf.Subject) {
-		return common.NewBasicError("Requester does not match subject", nil, "ia",
-			vChain.Leaf.Subject, "sub", c.Subject)
-	}
-	if maxChain.Leaf.Version+1 != c.Version {
-		return common.NewBasicError("Invalid version", nil, "expected", maxChain.Leaf.Version+1,
-			"actual", c.Version)
-	}
-	if !c.Issuer.Equal(h.IA) {
-		return common.NewBasicError("Requested Issuer is not this AS", nil, "iss",
-			c.Issuer, "expected", h.IA)
-	}
-	if c.CanIssue {
-		return serrors.New("CanIssue not allowed to be true")
-	}
-	if !bytes.Equal(vKey, vChain.Leaf.SubjectSignKey) {
-		return serrors.New("Request signed with wrong signing key")
-	}
-	return nil
-}
-
-// issueChain creates a certificate chain for the certificate and adds it to the
-// trust store.
-func (h *Handler) issueChain(ctx context.Context, c *cert.Certificate,
-	vKey common.RawBytes, verVersion scrypto.Version) (*cert.Chain, error) {
-
-	issCert, err := h.getIssuerCert(ctx)
-	if err != nil {
-		return nil, err
-	}
-	chain := &cert.Chain{Leaf: c.Copy(), Issuer: issCert}
-	chain.Leaf.CanIssue = false
-	chain.Leaf.TRCVersion = chain.Issuer.TRCVersion
-	chain.Leaf.IssuingTime = util.TimeToSecs(time.Now())
-	chain.Leaf.ExpirationTime = chain.Leaf.IssuingTime + cert.DefaultLeafCertValidity
-	// Leaf certificate must expire before issuer certificate
-	if chain.Issuer.ExpirationTime < chain.Leaf.ExpirationTime {
-		chain.Leaf.ExpirationTime = chain.Issuer.ExpirationTime
-	}
-	if err = chain.Leaf.Sign(h.State.GetIssSigningKey(), chain.Issuer.SignAlgorithm); err != nil {
-		return nil, err
-	}
-	err = chain.Leaf.Verify(c.Subject, issCert.SubjectSignKey, issCert.SignAlgorithm)
-	if err != nil {
-		return nil, err
-	}
-	tx, err := h.State.TrustDB.BeginTransaction(ctx, nil)
-	if err != nil {
-		return nil, common.NewBasicError("Failed to create transaction", err)
-	}
-	// Set verifying key.
-	newCustKey := &trustdb.CustKey{IA: c.Subject, Key: c.SubjectSignKey, Version: c.Version}
-	err = tx.InsertCustKey(ctx, newCustKey, verVersion)
-	if err != nil {
-		tx.Rollback()
-		return nil, err
-	}
-	var n int64
-	if n, err = tx.InsertChain(ctx, chain); err != nil {
-		tx.Rollback()
-		log.FromCtx(ctx).Error("[reiss.Handler] Unable to write reissued certificate chain to disk",
-			"err", err)
-		return nil, err
-	}
-	if n == 0 {
-		tx.Rollback()
-		return nil, common.NewBasicError("Chain already in DB", nil, "chain", chain)
-	}
-	if err = tx.Commit(); err != nil {
-		return nil, common.NewBasicError("Failed to commit transaction", err)
-	}
-	return chain, nil
-}
-
-func (h *Handler) sendRep(ctx context.Context, addr net.Addr, chain *cert.Chain) error {
-
-	raw, err := chain.Compress()
-	if err != nil {
-		return err
-	}
-	rw, ok := infra.ResponseWriterFromContext(ctx)
-	if !ok {
-		return serrors.New("Unable to send reply, no response writer found")
-	}
-	log.Trace("[reiss.Handler] Sending reissued certificate chain", "chain", chain,
-		"addr", addr)
-	return rw.SendChainIssueReply(ctx, &cert_mgmt.ChainIssRep{RawChain: raw})
-}
-
-func (h *Handler) getIssuerCert(ctx context.Context) (*cert.Certificate, error) {
-	issCrt, err := h.State.TrustDB.GetIssCertMaxVersion(ctx, h.IA)
-	if err != nil {
-		return nil, err
-	}
-	if issCrt == nil {
-		return nil, common.NewBasicError("Issuer certificate not found", nil, "ia", h.IA)
-	}
-	return issCrt, nil
-}
-
-// getVerifyingKey returns the verifying key from the requested AS and nil if it is in the mapping.
-// Otherwise, nil and an error.
-func (h *Handler) getVerifyingKey(ctx context.Context,
-	ia addr.IA) (common.RawBytes, scrypto.Version, error) {
-
-	k, err := h.State.TrustDB.GetCustKey(ctx, ia)
-	if err != nil {
-		return nil, 0, err
-	}
-	if k == nil {
-		return nil, 0, common.NewBasicError(ErrNotACustomer, nil, "ISD-AS", ia)
-	}
-	return k.Key, k.Version, nil
-}
diff --git a/go/cert_srv/internal/reiss/requester.go b/go/cert_srv/internal/reiss/requester.go
deleted file mode 100644
index e4f553a69..000000000
--- a/go/cert_srv/internal/reiss/requester.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2018 ETH Zurich, 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 reiss
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"time"
-
-	"golang.org/x/crypto/ed25519"
-
-	"github.com/scionproto/scion/go/cert_srv/internal/config"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/cert_mgmt"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/snet"
-	"github.com/scionproto/scion/go/lib/util"
-)
-
-var _ periodic.Task = (*Requester)(nil)
-
-// Requester requests reissued certificate chains before
-// expiration of the currently active certificate chain.
-type Requester struct {
-	Msgr       infra.Messenger
-	State      *config.State
-	IA         addr.IA
-	LeafTime   time.Duration
-	CorePusher *periodic.Runner
-	Caller     string
-}
-
-// Name returns the tasks name.
-func (r *Requester) Name() string {
-	return fmt.Sprintf("%s_reiss_requester", r.Caller)
-}
-
-// Run requests reissued certificate chains from the issuer AS.
-func (r *Requester) Run(ctx context.Context) {
-	logger := log.FromCtx(ctx)
-	crit, err := r.run(ctx)
-	switch {
-	case crit && err != nil:
-		logger.Crit("[reiss.Requester] Unable to get reissued certificate chain", "err", err)
-	case err != nil:
-		logger.Error("[reiss.Requester] Unable to get reissued certificate chain", "err", err)
-	}
-}
-
-func (r *Requester) run(ctx context.Context) (bool, error) {
-	opts := infra.ChainOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	chain, err := r.State.Store.GetChain(ctx, r.IA, scrypto.LatestVer, opts)
-	if err != nil {
-		return true, common.NewBasicError("Unable to get local certificate chain", err)
-	}
-	exp := util.SecsToTime(chain.Leaf.ExpirationTime)
-	now := time.Now()
-	if now.After(exp) {
-		return true, common.NewBasicError("Certificate expired without being reissued", nil,
-			"chain", chain, "expTime", util.TimeToCompact(exp), "now", util.TimeToString(now))
-	}
-	if now.Add(r.LeafTime).Before(exp) {
-		return false, nil
-	}
-	return r.sendReq(ctx, chain)
-}
-
-// sendReq creates and sends a certificate chain reissue request based on the newest
-// currently active certificate chain.
-func (r *Requester) sendReq(ctx context.Context, chain *cert.Chain) (bool, error) {
-	logger := log.FromCtx(ctx)
-	c := chain.Leaf.Copy()
-	c.IssuingTime = util.TimeToSecs(time.Now())
-	c.ExpirationTime = c.IssuingTime + (chain.Leaf.ExpirationTime - chain.Leaf.IssuingTime)
-	c.Version++
-	if err := c.Sign(r.State.GetSigningKey(), chain.Leaf.SignAlgorithm); err != nil {
-		return true, common.NewBasicError("Unable to sign certificate", err)
-	}
-	raw, err := c.JSON(false)
-	if err != nil {
-		return false, common.NewBasicError("Unable to pack certificate", err)
-	}
-	req := &cert_mgmt.ChainIssReq{RawCert: raw}
-	a := snet.NewSVCAddr(c.Issuer, nil, nil, addr.SvcCS)
-	rep, err := r.Msgr.RequestChainIssue(ctx, req, a, messenger.NextId())
-	if err != nil {
-		return false, common.NewBasicError("Unable to request reissued certificate chain", err)
-	}
-	logger.Trace("[reiss.Requester] Received certificate reissue reply", "addr", a, "rep", rep)
-	if crit, err := r.handleRep(ctx, rep); err != nil {
-		return crit, common.NewBasicError("Unable to handle reply", err, "addr", a, "rep", rep)
-	}
-	return false, nil
-}
-
-func (r *Requester) handleRep(ctx context.Context, rep *cert_mgmt.ChainIssRep) (bool, error) {
-	logger := log.FromCtx(ctx)
-	chain, err := rep.Chain()
-	if err != nil {
-		return false, common.NewBasicError("Unable to parse chain", err)
-	}
-	if err = r.validateRep(ctx, chain); err != nil {
-		return true, common.NewBasicError("Unable to validate chain", err, "chain", chain)
-	}
-	if _, err = r.State.TrustDB.InsertChain(ctx, chain); err != nil {
-		return true, common.NewBasicError("Unable to insert reissued certificate chain in TrustDB",
-			err, "chain", chain)
-	}
-	meta, err := trust.CreateSignMeta(ctx, r.IA, r.State.TrustDB)
-	if err != nil {
-		return true, common.NewBasicError("Unable create sign meta", err)
-	}
-	signer, err := trust.NewBasicSigner(r.State.GetSigningKey(), meta)
-	if err != nil {
-		return true, common.NewBasicError("Unable to create new signer", err)
-	}
-	r.State.SetSigner(signer)
-	r.Msgr.UpdateSigner(signer, []infra.MessageType{infra.ChainIssueRequest})
-	logger.Info("[reiss.Requester] Updated certificate chain", "chain", chain)
-	if r.CorePusher != nil {
-		r.CorePusher.TriggerRun()
-	}
-	return false, nil
-}
-
-// validateRep validates that the received certificate chain can be added to the trust store.
-func (r *Requester) validateRep(ctx context.Context, chain *cert.Chain) error {
-	verKey := common.RawBytes(ed25519.PrivateKey(
-		r.State.GetSigningKey()).Public().(ed25519.PublicKey))
-	if !bytes.Equal(chain.Leaf.SubjectSignKey, verKey) {
-		return common.NewBasicError("Invalid SubjectSignKey", nil, "expected",
-			verKey, "actual", chain.Leaf.SubjectSignKey)
-	}
-	// FIXME(roosd): validate SubjectEncKey
-	opts := infra.ChainOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	chain, err := r.State.Store.GetChain(ctx, r.IA, scrypto.LatestVer, opts)
-	if err != nil {
-		return err
-	}
-	issuer := chain.Leaf.Issuer
-	if !chain.Leaf.Issuer.Equal(issuer) {
-		return common.NewBasicError("Invalid Issuer", nil, "expected",
-			issuer, "actual", chain.Leaf.Issuer)
-	}
-	return trust.VerifyChain(ctx, r.IA, chain, r.State.Store)
-}
diff --git a/go/cert_srv/internal/reiss/self.go b/go/cert_srv/internal/reiss/self.go
deleted file mode 100644
index ad68989a9..000000000
--- a/go/cert_srv/internal/reiss/self.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2018 ETH Zurich, 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 reiss
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"github.com/scionproto/scion/go/cert_srv/internal/config"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/scrypto/trc"
-	"github.com/scionproto/scion/go/lib/util"
-)
-
-var _ periodic.Task = (*Self)(nil)
-
-// Self periodically issues self-signed certificate chains
-// on an issuer AS before the old one expires.
-type Self struct {
-	// Msgr is used to propagate key updates to the messenger, and not for network traffic
-	Msgr       infra.Messenger
-	State      *config.State
-	IA         addr.IA
-	IssTime    time.Duration
-	LeafTime   time.Duration
-	CorePusher *periodic.Runner
-	Caller     string
-}
-
-// Name returns the tasks name.
-func (s *Self) Name() string {
-	return fmt.Sprintf("%s_reiss_self", s.Caller)
-}
-
-// Run issues certificate chains for the local AS.
-func (s *Self) Run(ctx context.Context) {
-	if err := s.run(ctx); err != nil {
-		log.FromCtx(ctx).Crit("[reiss.Self] Unable to self issue", "err", err)
-	}
-}
-
-func (s *Self) run(ctx context.Context) error {
-	issCrt, err := s.getIssuerCert(ctx)
-	if err != nil {
-		return common.NewBasicError("Unable to get issuer certificate", err)
-	}
-	opts := infra.ChainOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	chain, err := s.State.Store.GetChain(ctx, s.IA, scrypto.LatestVer, opts)
-	if err != nil {
-		return common.NewBasicError("Unable to get certificate chain", err)
-	}
-	now := time.Now()
-	iSleep := time.Unix(int64(issCrt.ExpirationTime), 0).Sub(now) - s.IssTime
-	lSleep := time.Unix(int64(chain.Leaf.ExpirationTime), 0).Sub(now) - s.LeafTime
-	if lSleep > 0 && iSleep > 0 {
-		return nil
-	}
-	if iSleep <= 0 {
-		// The issuer certificated needs to be updated.
-		if err = s.createIssuerCert(ctx, issCrt); err != nil {
-			return common.NewBasicError("Unable to create issuer certificate", err)
-		}
-	}
-	if lSleep <= 0 {
-		if err = s.createLeafCert(ctx, chain.Leaf); err != nil {
-			return common.NewBasicError("Unable to issue certificate chain", err)
-		}
-	}
-	if s.CorePusher != nil {
-		s.CorePusher.TriggerRun()
-	}
-	return nil
-}
-
-// createLeafCert creates a leaf certificate.
-func (s *Self) createLeafCert(ctx context.Context, leaf *cert.Certificate) error {
-	issCrt, err := s.getIssuerCert(ctx)
-	if err != nil {
-		return common.NewBasicError("Unable to get issuer certificate", err)
-	}
-	chain := &cert.Chain{Leaf: leaf.Copy(), Issuer: issCrt}
-	chain.Leaf.Version++
-	chain.Leaf.IssuingTime = util.TimeToSecs(time.Now())
-	chain.Leaf.CanIssue = false
-	chain.Leaf.ExpirationTime = chain.Leaf.IssuingTime + cert.DefaultLeafCertValidity
-	if chain.Issuer.ExpirationTime < chain.Leaf.ExpirationTime {
-		chain.Leaf.ExpirationTime = chain.Issuer.ExpirationTime
-	}
-	if err := chain.Leaf.Sign(s.State.GetIssSigningKey(), issCrt.SignAlgorithm); err != nil {
-		return common.NewBasicError("Unable to sign leaf certificate", err, "chain", chain)
-	}
-	if err := trust.VerifyChain(ctx, s.IA, chain, s.State.Store); err != nil {
-		return common.NewBasicError("Unable to verify chain", err, "chain", chain)
-	}
-	if _, err := s.State.TrustDB.InsertChain(ctx, chain); err != nil {
-		return common.NewBasicError("Unable to write certificate chain", err, "chain", chain)
-	}
-	log.FromCtx(ctx).Info("[reiss.Self] Created certificate chain", "chain", chain)
-	meta, err := trust.CreateSignMeta(ctx, s.IA, s.State.TrustDB)
-	if err != nil {
-		return common.NewBasicError("Unable to create sign meta", err)
-	}
-	signer, err := trust.NewBasicSigner(s.State.GetSigningKey(), meta)
-	if err != nil {
-		return common.NewBasicError("Unable to create new signer", err)
-	}
-	s.State.SetSigner(signer)
-	s.Msgr.UpdateSigner(signer, []infra.MessageType{infra.ChainIssueReply})
-	return nil
-}
-
-func (s *Self) getIssuerCert(ctx context.Context) (*cert.Certificate, error) {
-	issCrt, err := s.State.TrustDB.GetIssCertMaxVersion(ctx, s.IA)
-	if err != nil {
-		return nil, err
-	}
-	if issCrt == nil {
-		return nil, common.NewBasicError("Issuer certificate not found", nil, "ia", s.IA)
-	}
-	return issCrt, nil
-}
-
-// createIssuerCert creates an issuer certificate.
-func (s *Self) createIssuerCert(ctx context.Context, crt *cert.Certificate) error {
-	crt = crt.Copy()
-	crt.Version += 1
-	crt.IssuingTime = util.TimeToSecs(time.Now())
-	crt.CanIssue = true
-	crt.ExpirationTime = crt.IssuingTime + cert.DefaultIssuerCertValidity
-	coreAS, err := s.getCoreASEntry(ctx)
-	if err != nil {
-		return common.NewBasicError("Unable to get core AS entry", err, "cert", crt)
-	}
-	if err = crt.Sign(s.State.GetOnRootKey(), coreAS.OnlineKeyAlg); err != nil {
-		return common.NewBasicError("Unable to sign issuer certificate", err, "cert", crt)
-	}
-	if err = crt.Verify(crt.Issuer, coreAS.OnlineKey, coreAS.OnlineKeyAlg); err != nil {
-		return common.NewBasicError("Invalid issuer certificate signature", err, "cert", crt)
-	}
-	if err = s.setIssuerCert(ctx, crt); err != nil {
-		return common.NewBasicError("Unable to store issuer certificate", err, "cert", crt)
-	}
-	log.FromCtx(ctx).Info("[reiss.Self] Created issuer certificate", "cert", crt)
-	return nil
-}
-
-func (s *Self) getCoreASEntry(ctx context.Context) (*trc.CoreAS, error) {
-	opts := infra.TRCOpts{TrustStoreOpts: infra.TrustStoreOpts{LocalOnly: true}}
-	maxTrc, err := s.State.Store.GetTRC(ctx, s.IA.I, scrypto.LatestVer, opts)
-	if err != nil {
-		return nil, common.NewBasicError("Unable to find local TRC", err)
-	}
-	coreAS := maxTrc.CoreASes[s.IA]
-	if coreAS == nil {
-		return nil, common.NewBasicError("Local AS is not a core AS in the max TRC",
-			nil, "maxTrc", maxTrc)
-	}
-	return coreAS, nil
-}
-
-func (s *Self) setIssuerCert(ctx context.Context, crt *cert.Certificate) error {
-	affected, err := s.State.TrustDB.InsertIssCert(ctx, crt)
-	if err != nil {
-		return err
-	}
-	if affected == 0 {
-		return common.NewBasicError("Issuer certificate already exists", nil, "cert", crt)
-	}
-	return nil
-}
diff --git a/go/cert_srv/main.go b/go/cert_srv/main.go
index 5a0ddda63..a87dd5e8c 100644
--- a/go/cert_srv/main.go
+++ b/go/cert_srv/main.go
@@ -23,13 +23,11 @@ import (
 	"os"
 	"path/filepath"
 	"sync"
-	"time"
 
 	"github.com/BurntSushi/toml"
 	"github.com/opentracing/opentracing-go"
 
 	"github.com/scionproto/scion/go/cert_srv/internal/config"
-	"github.com/scionproto/scion/go/cert_srv/internal/reiss"
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/discovery"
@@ -40,8 +38,8 @@ import (
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
+	"github.com/scionproto/scion/go/lib/keyconf"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/periodic"
 	"github.com/scionproto/scion/go/lib/prom"
@@ -84,12 +82,12 @@ func realMain() int {
 		log.Crit("Setup failed", "err", err)
 		return 1
 	}
+
 	topo := itopo.Get()
 	if !topo.Exists(addr.SvcCS, cfg.General.ID) {
 		log.Crit("Unable to find topo address")
 		return 1
 	}
-
 	tracer, trCloser, err := cfg.Tracing.NewTracer(cfg.General.ID)
 	if err != nil {
 		log.Crit("Unable to create tracer", "err", err)
@@ -103,31 +101,6 @@ func realMain() int {
 		log.Crit("Unable to initialize path router", "err", err)
 		return 1
 	}
-	trustDB, err := cfg.TrustDB.New()
-	if err != nil {
-		log.Crit("Unable to initialize trustDB", "err", err)
-		return 1
-	}
-	trustDB = trustdb.WithMetrics(string(cfg.TrustDB.Backend()), trustDB)
-	defer trustDB.Close()
-	trustConf := trust.Config{
-		MustHaveLocalChain: true,
-		ServiceType:        proto.ServiceType_cs,
-		Router:             router,
-		TopoProvider:       itopo.Provider(),
-	}
-	trustStore := trust.NewStore(trustDB, topo.IA(), trustConf, log.Root())
-	err = trustStore.LoadAuthoritativeCrypto(filepath.Join(cfg.General.ConfigDir, "certs"))
-	if err != nil {
-		log.Crit("Unable to load local crypto", "err", err)
-		return 1
-	}
-
-	state, err := newState(topo, trustDB, trustStore)
-	if err != nil {
-		log.Crit("Unable to load state", "err", err)
-		return 1
-	}
 
 	nc := infraenv.NetworkConfig{
 		IA:                    topo.IA(),
@@ -140,7 +113,6 @@ func realMain() int {
 			KeyFile:  cfg.QUIC.KeyFile,
 		},
 		SVCResolutionFraction: cfg.QUIC.ResolutionFraction,
-		TrustStore:            trustStore,
 		Router:                router,
 		SVCRouter:             messenger.NewSVCRouter(itopo.Provider()),
 	}
@@ -151,25 +123,66 @@ func realMain() int {
 	}
 	defer msgr.CloseServer()
 
-	msgr.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler(true))
-	msgr.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler(true))
+	trustDB, err := cfg.TrustDB.New()
+	if err != nil {
+		log.Crit("Error initializing trust database", "err", err)
+		return 1
+	}
+	defer trustDB.Close()
+	inserter := trust.DefaultInserter{
+		BaseInserter: trust.BaseInserter{DB: trustDB},
+	}
+	provider := trust.Provider{
+		DB:       trustDB,
+		Recurser: trust.ASLocalRecurser{IA: topo.IA()},
+		Resolver: trust.DefaultResolver{
+			DB:       trustDB,
+			Inserter: inserter,
+			RPC:      trust.DefaultRPC{Msgr: msgr},
+		},
+		Router: trust.AuthRouter{
+			ISD:    topo.IA().I,
+			Router: router,
+			DB:     trustDB,
+		},
+	}
+	trustStore := trust.Store{
+		Inspector:      trust.DefaultInspector{Provider: provider},
+		CryptoProvider: provider,
+		Inserter:       inserter,
+		DB:             trustDB,
+	}
+	certsDir := filepath.Join(cfg.General.ConfigDir, "certs")
+	if err = trustStore.LoadCryptoMaterial(context.Background(), certsDir); err != nil {
+		log.Crit("Error loading crypto material", "err", err)
+		return 1
+	}
+	gen := trust.SignerGen{
+		IA: topo.IA(),
+		KeyRing: keyconf.LoadingRing{
+			Dir: filepath.Join(cfg.General.ConfigDir, "keys"),
+			IA:  topo.IA(),
+		},
+		Provider: trustStore,
+	}
+	signer, err := gen.Signer(context.Background())
+	if err != nil {
+		log.Crit("Error initializing signer", "err", err)
+		return 1
+	}
+
+	msgr.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler())
+	msgr.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler())
 	msgr.AddHandler(infra.Chain, trustStore.NewChainPushHandler())
 	msgr.AddHandler(infra.TRC, trustStore.NewTRCPushHandler())
-	msgr.UpdateSigner(state.GetSigner(), []infra.MessageType{infra.ChainIssueRequest})
-	msgr.UpdateVerifier(trust.NewBasicVerifier(trustStore))
-	// Only core CS handles certificate reissuance requests.
-	if topo.Core() {
-		msgr.AddHandler(infra.ChainIssueRequest, &reiss.Handler{
-			State: state,
-			IA:    topo.IA(),
-		})
-	}
+	msgr.UpdateSigner(signer, []infra.MessageType{infra.ChainIssueRequest})
+	msgr.UpdateVerifier(trust.NewVerifier(trustStore))
 
+	// Start the messenger.
 	go func() {
 		defer log.LogPanicAndExit()
 		msgr.ListenAndServe()
 	}()
-	cfg.Metrics.StartPrometheus()
 
 	discoRunners, err := idiscovery.StartRunners(cfg.Discovery, discovery.Full,
 		idiscovery.TopoHandlers{}, nil, "cs")
@@ -183,7 +196,6 @@ func realMain() int {
 		TopoProvider: itopo.Provider(),
 		Msgr:         msgr,
 		TrustDB:      trustDB,
-		State:        state,
 	}
 	if err := tasks.Start(); err != nil {
 		log.Crit("Unable to start periodic tasks", "err", err)
@@ -202,8 +214,7 @@ func realMain() int {
 type periodicTasks struct {
 	TopoProvider topology.Provider
 	Msgr         infra.Messenger
-	TrustDB      trustdb.TrustDB
-	State        *config.State
+	TrustDB      trust.DB
 
 	corePusher *periodic.Runner
 	reissuance *periodic.Runner
@@ -220,64 +231,12 @@ func (t *periodicTasks) Start() error {
 		return nil
 	}
 	t.running = true
-	t.corePusher = t.startCorePusher()
-	t.reissuance = t.startReissuance(t.corePusher)
+	// t.corePusher = t.startCorePusher()
+	// t.reissuance = t.startReissuance(t.corePusher)
 	log.Info("Started periodic tasks")
 	return nil
 }
 
-func (t *periodicTasks) startCorePusher() *periodic.Runner {
-	if cfg.CS.DisableCorePush {
-		return nil
-	}
-	p := periodic.Start(
-		&reiss.CorePusher{
-			LocalIA: t.TopoProvider.Get().IA(),
-			TrustDB: t.TrustDB,
-			Msger:   t.Msgr,
-		},
-		time.Hour,
-		time.Minute,
-	)
-	p.TriggerRun()
-	return p
-}
-
-func (t *periodicTasks) startReissuance(corePusher *periodic.Runner) *periodic.Runner {
-	if !cfg.CS.AutomaticRenewal {
-		log.Info("Certificate reissuance disabled, not starting periodic task.")
-		return nil
-	}
-	if t.TopoProvider.Get().Core() {
-		log.Info("Starting periodic self-issuing reissuance task.")
-		return periodic.Start(
-			&reiss.Self{
-				Msgr:       t.Msgr,
-				State:      t.State,
-				IA:         t.TopoProvider.Get().IA(),
-				IssTime:    cfg.CS.IssuerReissueLeadTime.Duration,
-				LeafTime:   cfg.CS.LeafReissueLeadTime.Duration,
-				CorePusher: corePusher,
-				Caller:     "cs",
-			},
-			cfg.CS.ReissueRate.Duration,
-			cfg.CS.ReissueTimeout.Duration,
-		)
-	}
-	log.Info("Starting periodic reissuance requester task.")
-	return periodic.Start(
-		&reiss.Requester{
-			Msgr:       t.Msgr,
-			State:      t.State,
-			IA:         t.TopoProvider.Get().IA(),
-			LeafTime:   cfg.CS.LeafReissueLeadTime.Duration,
-			CorePusher: corePusher,
-		},
-		cfg.CS.ReissueRate.Duration,
-		cfg.CS.ReissueTimeout.Duration,
-	)
-}
-
 func (t *periodicTasks) Kill() {
 	t.mtx.Lock()
 	defer t.mtx.Unlock()
@@ -330,26 +289,3 @@ func setup() error {
 	infraenv.InitInfraEnvironment(cfg.General.Topology)
 	return nil
 }
-
-// TODO(roosd): Remove with trust store v2
-func newState(topo topology.Topology, db trustdb.TrustDB,
-	store *trust.Store) (*config.State, error) {
-
-	state, err := config.LoadState(cfg.General.ConfigDir, topo.Core(), db, store)
-	if err != nil {
-		return nil, serrors.WrapStr("unable to load CS state", err)
-	}
-	ctx, cancelF := context.WithTimeout(context.Background(), 2*time.Second)
-	defer cancelF()
-	meta, err := trust.CreateSignMeta(ctx, topo.IA(), db)
-	if err != nil {
-		return nil, err
-	}
-	signer, err := trust.NewBasicSigner(state.GetSigningKey(), meta)
-	if err != nil {
-		return nil, err
-	}
-	state.SetSigner(signer)
-	state.SetVerifier(state.Store.NewVerifier())
-	return state, nil
-}
diff --git a/go/integration/cert_req/BUILD.bazel b/go/integration/cert_req/BUILD.bazel
index 2eb0f99e6..a05068e43 100644
--- a/go/integration/cert_req/BUILD.bazel
+++ b/go/integration/cert_req/BUILD.bazel
@@ -17,8 +17,8 @@ go_library(
         "//go/lib/log:go_default_library",
         "//go/lib/sciond:go_default_library",
         "//go/lib/scrypto:go_default_library",
-        "//go/lib/scrypto/cert:go_default_library",
-        "//go/lib/serrors:go_default_library",
+        "//go/lib/scrypto/cert/v2:go_default_library",
+        "//go/lib/scrypto/trc/v2:go_default_library",
         "//go/lib/snet:go_default_library",
     ],
 )
diff --git a/go/integration/cert_req/main.go b/go/integration/cert_req/main.go
index e5d14e675..11ffc608b 100644
--- a/go/integration/cert_req/main.go
+++ b/go/integration/cert_req/main.go
@@ -31,8 +31,8 @@ import (
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/sciond"
 	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/lib/scrypto/cert/v2"
+	"github.com/scionproto/scion/go/lib/scrypto/trc/v2"
 	"github.com/scionproto/scion/go/lib/snet"
 )
 
@@ -109,9 +109,8 @@ func (c client) attemptRequest(n int) bool {
 
 func (c client) requestCert() (*cert.Chain, error) {
 	req := &cert_mgmt.ChainReq{
-		CacheOnly: false,
-		RawIA:     remoteIA.IAInt(),
-		Version:   scrypto.LatestVer,
+		RawIA:   remoteIA.IAInt(),
+		Version: scrypto.LatestVer,
 	}
 	log.Info("Request to SVC: Chain request", "req", req, "svc", svc)
 	ctx, cancelF := context.WithTimeout(context.Background(), integration.DefaultIOTimeout)
@@ -120,26 +119,26 @@ func (c client) requestCert() (*cert.Chain, error) {
 	if err != nil {
 		return nil, common.NewBasicError("Unable to get chain", err)
 	}
-	chain, err := rawChain.Chain()
+	chain, err := cert.ParseChain(rawChain.RawChain)
 	if err != nil {
 		return nil, common.NewBasicError("Unable to parse chain", err)
 	}
-	if chain == nil {
-		return nil, serrors.New("Empty reply")
+	as, err := chain.AS.Encoded.Decode()
+	if err != nil {
+		return nil, common.NewBasicError("Unable to parse AS certificate", err)
 	}
-	if !chain.Leaf.Subject.Equal(remoteIA) {
+	if !as.Subject.Equal(remoteIA) {
 		return nil, common.NewBasicError("Invalid subject", nil,
-			"expected", remoteIA, "actual", chain.Leaf.Subject)
+			"expected", remoteIA, "actual", as.Subject)
 	}
 	log.Info("Response from SVC: Correct chain", "chain", chain)
-	return chain, nil
+	return &chain, nil
 }
 
 func (c client) requestTRC(chain *cert.Chain) error {
 	req := &cert_mgmt.TRCReq{
-		CacheOnly: false,
-		ISD:       remoteIA.I,
-		Version:   scrypto.LatestVer,
+		ISD:     remoteIA.I,
+		Version: scrypto.LatestVer,
 	}
 	log.Info("Request to SVC: TRC request", "req", req, "svc", svc)
 	ctx, cancelF := context.WithTimeout(context.Background(), integration.DefaultIOTimeout)
@@ -148,24 +147,59 @@ func (c client) requestTRC(chain *cert.Chain) error {
 	if err != nil {
 		return common.NewBasicError("Unable to get trc", err)
 	}
-	trc, err := rawTrc.TRC()
+	signed, err := trc.ParseSigned(rawTrc.RawTRC)
 	if err != nil {
-		return common.NewBasicError("Unable to parse trc", err)
+		return common.NewBasicError("Unable to parse signed trc", err)
 	}
-	if trc == nil {
-		return serrors.New("Empty reply")
+	trc, err := signed.EncodedTRC.Decode()
+	if err != nil {
+		return common.NewBasicError("Unable to parse trc payload", err)
 	}
 	if trc.ISD != remoteIA.I {
 		return common.NewBasicError("Invalid ISD", nil,
 			"expected", remoteIA.I, "actual", trc.ISD)
 	}
-	if err := chain.Verify(remoteIA, trc); err != nil {
-		return common.NewBasicError("Certificate verification failed", err)
+	if err := c.verifyChain(chain, trc); err != nil {
+		return common.NewBasicError("unable to verify chain", err)
 	}
 	log.Info("Response from SVC: Correct TRC", "TRC", trc)
 	return nil
 }
 
+func (c client) verifyChain(chain *cert.Chain, t *trc.TRC) error {
+	as, err := chain.AS.Encoded.Decode()
+	if err != nil {
+		return common.NewBasicError("unable to parse AS certificate", err)
+	}
+	if err := as.Validate(); err != nil {
+		return common.NewBasicError("unable to validate AS certificate", err)
+	}
+	iss, err := chain.Issuer.Encoded.Decode()
+	if err != nil {
+		return common.NewBasicError("unable to parse issuer certificate", err)
+	}
+	if err := iss.Validate(); err != nil {
+		return common.NewBasicError("unable to validate issuer certificate", err)
+	}
+	issVerifier := cert.IssuerVerifier{
+		Issuer:       iss,
+		SignedIssuer: &chain.Issuer,
+		TRC:          t,
+	}
+	if err := issVerifier.Verify(); err != nil {
+		return common.NewBasicError("unable to verify issuer certificate", err)
+	}
+	asVerifier := cert.ASVerifier{
+		Issuer:   iss,
+		SignedAS: &chain.AS,
+		AS:       as,
+	}
+	if err := asVerifier.Verify(); err != nil {
+		return common.NewBasicError("unable to verify issuer certificate", err)
+	}
+	return nil
+}
+
 func getRemote() error {
 	// Fetch address of service
 	var svcHost *net.UDPAddr
diff --git a/go/lib/ctrl/cert_mgmt/BUILD.bazel b/go/lib/ctrl/cert_mgmt/BUILD.bazel
index e36648b36..53e955c9c 100644
--- a/go/lib/ctrl/cert_mgmt/BUILD.bazel
+++ b/go/lib/ctrl/cert_mgmt/BUILD.bazel
@@ -18,7 +18,9 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/scrypto:go_default_library",
         "//go/lib/scrypto/cert:go_default_library",
+        "//go/lib/scrypto/cert/v2:go_default_library",
         "//go/lib/scrypto/trc:go_default_library",
+        "//go/lib/scrypto/trc/v2:go_default_library",
         "//go/proto:go_default_library",
     ],
 )
diff --git a/go/lib/ctrl/cert_mgmt/chain.go b/go/lib/ctrl/cert_mgmt/chain.go
index dbd7e8fde..56811abdd 100644
--- a/go/lib/ctrl/cert_mgmt/chain.go
+++ b/go/lib/ctrl/cert_mgmt/chain.go
@@ -19,7 +19,8 @@ import (
 	"fmt"
 
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
+	legacy "github.com/scionproto/scion/go/lib/scrypto/cert"
+	"github.com/scionproto/scion/go/lib/scrypto/cert/v2"
 	"github.com/scionproto/scion/go/proto"
 )
 
@@ -29,11 +30,11 @@ type Chain struct {
 	RawChain common.RawBytes `capnp:"chain"`
 }
 
-func (c *Chain) Chain() (*cert.Chain, error) {
+func (c *Chain) Chain() (*legacy.Chain, error) {
 	if c.RawChain == nil {
 		return nil, nil
 	}
-	return cert.ChainFromRaw(c.RawChain, true)
+	return legacy.ChainFromRaw(c.RawChain, true)
 }
 
 func (c *Chain) ProtoId() proto.ProtoIdType {
@@ -41,9 +42,13 @@ func (c *Chain) ProtoId() proto.ProtoIdType {
 }
 
 func (c *Chain) String() string {
-	chain, err := c.Chain()
+	raw, err := cert.ParseChain(c.RawChain)
 	if err != nil {
 		return fmt.Sprintf("Invalid CertificateChain: %v", err)
 	}
-	return chain.String()
+	as, err := raw.AS.Encoded.Decode()
+	if err != nil {
+		return fmt.Sprintf("Invalid AS certificate: %v", err)
+	}
+	return fmt.Sprintf("ISD%d-AS%s-V%d", as.Subject.I, as.Subject.A, as.Version)
 }
diff --git a/go/lib/ctrl/cert_mgmt/trc.go b/go/lib/ctrl/cert_mgmt/trc.go
index 2cadb3bcb..c992e25ac 100644
--- a/go/lib/ctrl/cert_mgmt/trc.go
+++ b/go/lib/ctrl/cert_mgmt/trc.go
@@ -19,7 +19,8 @@ import (
 	"fmt"
 
 	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/scrypto/trc"
+	legacy "github.com/scionproto/scion/go/lib/scrypto/trc"
+	"github.com/scionproto/scion/go/lib/scrypto/trc/v2"
 	"github.com/scionproto/scion/go/proto"
 )
 
@@ -29,11 +30,11 @@ type TRC struct {
 	RawTRC common.RawBytes `capnp:"trc"`
 }
 
-func (t *TRC) TRC() (*trc.TRC, error) {
+func (t *TRC) TRC() (*legacy.TRC, error) {
 	if t.RawTRC == nil {
 		return nil, nil
 	}
-	return trc.TRCFromRaw(t.RawTRC, true)
+	return legacy.TRCFromRaw(t.RawTRC, true)
 }
 
 func (t *TRC) ProtoId() proto.ProtoIdType {
@@ -41,9 +42,13 @@ func (t *TRC) ProtoId() proto.ProtoIdType {
 }
 
 func (t *TRC) String() string {
-	u, err := t.TRC()
+	signed, err := trc.ParseSigned(t.RawTRC)
 	if err != nil {
-		return fmt.Sprintf("Invalid TRC: %v", err)
+		return fmt.Sprintf("Invalid signed TRC: %v", err)
 	}
-	return u.String()
+	pld, err := signed.EncodedTRC.Decode()
+	if err != nil {
+		return fmt.Sprintf("Invalid TRC payload: %v", err)
+	}
+	return fmt.Sprintf("ISD%d-V%d", pld.ISD, pld.Version)
 }
diff --git a/go/lib/infra/infraenv/BUILD.bazel b/go/lib/infra/infraenv/BUILD.bazel
index bc3d8738c..2f4856228 100644
--- a/go/lib/infra/infraenv/BUILD.bazel
+++ b/go/lib/infra/infraenv/BUILD.bazel
@@ -12,7 +12,6 @@ go_library(
         "//go/lib/infra:go_default_library",
         "//go/lib/infra/disp:go_default_library",
         "//go/lib/infra/messenger:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/sciond:go_default_library",
         "//go/lib/snet:go_default_library",
diff --git a/go/lib/infra/infraenv/infraenv.go b/go/lib/infra/infraenv/infraenv.go
index fcb668912..581f0e614 100644
--- a/go/lib/infra/infraenv/infraenv.go
+++ b/go/lib/infra/infraenv/infraenv.go
@@ -30,7 +30,6 @@ import (
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/disp"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/sciond"
 	"github.com/scionproto/scion/go/lib/snet"
@@ -66,8 +65,6 @@ type NetworkConfig struct {
 	// SVC registers this server to receive packets with the specified SVC
 	// destination address.
 	SVC addr.HostSVC
-	// TrustStore is the crypto backend for control-plane verification.
-	TrustStore *trust.Store
 	// ReconnectToDispatcher sets up sockets that automatically reconnect if
 	// the dispatcher closes the connection (e.g., if the dispatcher goes
 	// down).
@@ -123,7 +120,6 @@ func (nc *NetworkConfig) Messenger() (infra.Messenger, error) {
 		}
 	}
 	msger := messenger.New(msgerCfg)
-	nc.TrustStore.SetMessenger(msger)
 	return msger, nil
 
 }
diff --git a/go/lib/infra/modules/trust/v2/BUILD.bazel b/go/lib/infra/modules/trust/v2/BUILD.bazel
index 440edbc8b..7efada00f 100644
--- a/go/lib/infra/modules/trust/v2/BUILD.bazel
+++ b/go/lib/infra/modules/trust/v2/BUILD.bazel
@@ -13,6 +13,7 @@ go_library(
         "router.go",
         "rpc.go",
         "signer.go",
+        "store.go",
         "verifier.go",
     ],
     importpath = "github.com/scionproto/scion/go/lib/infra/modules/trust/v2",
@@ -41,7 +42,6 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
-        "export_test.go",
         "handlers_test.go",
         "inserter_test.go",
         "inspector_test.go",
@@ -51,6 +51,7 @@ go_test(
         "resolver_test.go",
         "router_test.go",
         "signer_test.go",
+        "store_test.go",
         "verifier_export_test.go",
         "verifier_test.go",
     ],
@@ -67,6 +68,7 @@ go_test(
         "//go/lib/infra/mock_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",
+        "//go/lib/infra/modules/trust/v2/trustdbsqlite:go_default_library",
         "//go/lib/keyconf:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/scrypto:go_default_library",
diff --git a/go/lib/infra/modules/trust/v2/recurser.go b/go/lib/infra/modules/trust/v2/recurser.go
index f7eaaa6cc..e07c4c16b 100644
--- a/go/lib/infra/modules/trust/v2/recurser.go
+++ b/go/lib/infra/modules/trust/v2/recurser.go
@@ -18,6 +18,7 @@ import (
 	"net"
 
 	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/snet"
 )
@@ -48,10 +49,15 @@ func (r ASLocalRecurser) AllowRecursion(peer net.Addr) error {
 		return nil
 	}
 
+	// TODO(roosd): Remove when snet.Addr is removed.
+	if a, ok := peer.(*snet.Addr); ok {
+		peer = a.ToXAddr()
+	}
+
 	a, ok := peer.(*snet.UDPAddr)
 	if !ok {
 		return serrors.WrapStr("unable to determine AS of peer", ErrRecursionNotAllowed,
-			"addr", peer)
+			"addr", peer, "type", common.TypeOf(peer))
 	}
 	if !r.IA.Equal(a.IA) {
 		return serrors.WrapStr("client outside local AS", ErrRecursionNotAllowed,
diff --git a/go/lib/infra/modules/trust/v2/store.go b/go/lib/infra/modules/trust/v2/store.go
new file mode 100644
index 000000000..de7cf0a1f
--- /dev/null
+++ b/go/lib/infra/modules/trust/v2/store.go
@@ -0,0 +1,179 @@
+// 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
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"sort"
+
+	"github.com/scionproto/scion/go/lib/infra"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/internal/decoded"
+	"github.com/scionproto/scion/go/lib/serrors"
+)
+
+// Store keeps track of the control-plane PKI crypto material.
+type Store struct {
+	Inspector
+	CryptoProvider
+	Inserter Inserter
+	DB       DB
+}
+
+// NewTRCReqHandler returns an infra.Handler for TRC requests coming from a
+// peer, backed by the trust store. The configured recurser defines whether the
+// trust store is allowed to issue new TRC requests over the network.  This
+// method should only be used when servicing requests coming from remote nodes.
+func (s Store) NewTRCReqHandler() infra.Handler {
+	f := func(r *infra.Request) *infra.HandlerResult {
+		handler := &trcReqHandler{
+			request:  r,
+			provider: s.CryptoProvider,
+		}
+		return handler.Handle()
+	}
+	return infra.HandlerFunc(f)
+}
+
+// NewChainReqHandler returns an infra.Handler for Certificate Chain requests
+// coming from a peer, backed by the trust store. The configured recurser
+// defines whether the trust store is allowed to issue new TRC and certificate
+// chain requests over the network. This method should only be used when
+// servicing requests coming from remote nodes.
+func (s Store) NewChainReqHandler() infra.Handler {
+	f := func(r *infra.Request) *infra.HandlerResult {
+		handler := chainReqHandler{
+			request:  r,
+			provider: s.CryptoProvider,
+		}
+		return handler.Handle()
+	}
+	return infra.HandlerFunc(f)
+}
+
+// NewTRCPushHandler returns an infra.Handler for TRC pushes coming from a peer,
+// backed by the trust store. TRCs are pushed by local BSes and PSes. Pushes are
+// allowed from all local AS sources.
+func (s Store) NewTRCPushHandler() infra.Handler {
+	f := func(r *infra.Request) *infra.HandlerResult {
+		handler := trcPushHandler{
+			request:  r,
+			provider: s.CryptoProvider,
+			inserter: s.Inserter,
+		}
+		return handler.Handle()
+	}
+	return infra.HandlerFunc(f)
+}
+
+// NewChainPushHandler returns an infra.Handler for Certificate Chain pushes
+// coming from a peer, backed by the trust store. Certificate chains are pushed
+// by other ASes during core registration, or the local BSes and PSes. Pushes
+// are allowed from all local ISD sources.
+func (s Store) NewChainPushHandler() infra.Handler {
+	f := func(r *infra.Request) *infra.HandlerResult {
+		handler := chainPushHandler{
+			request:  r,
+			provider: s.CryptoProvider,
+			inserter: s.Inserter,
+		}
+		return handler.Handle()
+	}
+	return infra.HandlerFunc(f)
+}
+
+// LoadCryptoMaterial loads the crypto material from the file system and
+// populates the trust database.
+func (s Store) LoadCryptoMaterial(ctx context.Context, dir string) error {
+	if err := s.LoadTRCs(ctx, dir); err != nil {
+		return err
+	}
+	if err := s.LoadChains(ctx, dir); err != nil {
+		return err
+	}
+	return nil
+}
+
+// LoadTRCs loads the TRCs from the file system. This call ensures that the
+// hashes match for TRCs that are already in the database. Before insertion,
+// TRCs are verified.
+func (s Store) LoadTRCs(ctx context.Context, dir string) error {
+	files, err := filepath.Glob(fmt.Sprintf("%s/ISD*-V*.trc", dir))
+	if err != nil {
+		panic(err)
+	}
+	sort.Strings(files)
+	for _, file := range files {
+		if err := s.loadTRC(ctx, file); err != nil {
+			return serrors.WrapStr("unable to load TRC", err, "file", file)
+		}
+	}
+	return nil
+}
+
+func (s Store) loadTRC(ctx context.Context, file string) error {
+	raw, err := ioutil.ReadFile(file)
+	if err != nil {
+		return err
+	}
+	dec, err := decoded.DecodeTRC(raw)
+	if err != nil {
+		return err
+	}
+	unsafeInserter := DefaultInserter{
+		BaseInserter: BaseInserter{
+			DB:     s.DB,
+			Unsafe: true,
+		},
+	}
+	return unsafeInserter.InsertTRC(ctx, dec, s.DB.GetTRC)
+}
+
+// LoadChains loads the certificate chains from the file system. This call
+// ensures that the hashes match for the chains that are already in the
+// database. Before insertion, certificate chains are verified.
+func (s Store) LoadChains(ctx context.Context, dir string) error {
+	files, err := filepath.Glob(fmt.Sprintf("%s/ISD*-AS*-V*.crt", dir))
+	if err != nil {
+		panic(err)
+	}
+	sort.Strings(files)
+	for _, file := range files {
+		if err := s.loadChain(ctx, file); err != nil {
+			return serrors.WithCtx(err, "file", file)
+		}
+	}
+	return nil
+}
+
+func (s Store) loadChain(ctx context.Context, file string) error {
+	raw, err := ioutil.ReadFile(file)
+	if err != nil {
+		return err
+	}
+	dec, err := decoded.DecodeChain(raw)
+	if err != nil {
+		return err
+	}
+	unsafeInserter := DefaultInserter{
+		BaseInserter: BaseInserter{
+			DB:     s.DB,
+			Unsafe: true,
+		},
+	}
+	return unsafeInserter.InsertChain(ctx, dec, s.DB.GetTRC)
+}
diff --git a/go/lib/truststorage/BUILD.bazel b/go/lib/truststorage/BUILD.bazel
index 788a65347..0cc743ae0 100644
--- a/go/lib/truststorage/BUILD.bazel
+++ b/go/lib/truststorage/BUILD.bazel
@@ -12,8 +12,8 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/config:go_default_library",
         "//go/lib/infra/modules/db:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb/trustdbsqlite:go_default_library",
+        "//go/lib/infra/modules/trust/v2:go_default_library",
+        "//go/lib/infra/modules/trust/v2/trustdbsqlite:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/util:go_default_library",
diff --git a/go/lib/truststorage/truststorage.go b/go/lib/truststorage/truststorage.go
index d5732031c..5d5660650 100644
--- a/go/lib/truststorage/truststorage.go
+++ b/go/lib/truststorage/truststorage.go
@@ -24,8 +24,8 @@ import (
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/infra/modules/db"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb/trustdbsqlite"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/trustdbsqlite"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/util"
@@ -104,11 +104,11 @@ func (cfg *TrustDBConf) validateBackend() error {
 	return common.NewBasicError("Unsupported backend", nil, "backend", cfg.Backend())
 }
 
-// New creates a TrustDB from the config.
-func (cfg *TrustDBConf) New() (trustdb.TrustDB, error) {
+// New creates a trust database from the config.
+func (cfg *TrustDBConf) New() (trust.DB, error) {
 	log.Info("Connecting TrustDB", "backend", cfg.Backend(), "connection", cfg.Connection())
 	var err error
-	var tdb trustdb.TrustDB
+	var tdb trust.DB
 
 	switch cfg.Backend() {
 	case BackendSqlite:
diff --git a/go/path_srv/BUILD.bazel b/go/path_srv/BUILD.bazel
index 8139e9456..fc0e7adb1 100644
--- a/go/path_srv/BUILD.bazel
+++ b/go/path_srv/BUILD.bazel
@@ -17,17 +17,16 @@ go_library(
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
-        "//go/lib/infra/modules/trust/trustdb:go_default_library",
+        "//go/lib/infra/modules/trust/v2:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/pathdb:go_default_library",
         "//go/lib/pathstorage:go_default_library",
         "//go/lib/periodic:go_default_library",
         "//go/lib/prom:go_default_library",
         "//go/lib/revcache:go_default_library",
+        "//go/lib/serrors:go_default_library",
         "//go/lib/topology:go_default_library",
         "//go/path_srv/internal/config:go_default_library",
-        "//go/path_srv/internal/cryptosyncer:go_default_library",
         "//go/path_srv/internal/handlers:go_default_library",
         "//go/path_srv/internal/segreq:go_default_library",
         "//go/path_srv/internal/segsyncer:go_default_library",
diff --git a/go/path_srv/main.go b/go/path_srv/main.go
index 201788226..8be803477 100644
--- a/go/path_srv/main.go
+++ b/go/path_srv/main.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"context"
 	"flag"
 	"fmt"
 	_ "net/http/pprof"
@@ -36,17 +37,16 @@ import (
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/pathstorage"
 	"github.com/scionproto/scion/go/lib/periodic"
 	"github.com/scionproto/scion/go/lib/prom"
 	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/path_srv/internal/config"
-	"github.com/scionproto/scion/go/path_srv/internal/cryptosyncer"
 	"github.com/scionproto/scion/go/path_srv/internal/handlers"
 	"github.com/scionproto/scion/go/path_srv/internal/segreq"
 	"github.com/scionproto/scion/go/path_srv/internal/segsyncer"
@@ -93,25 +93,8 @@ func realMain() int {
 	defer revCache.Close()
 	pathDB = pathdb.WithMetrics("std", pathDB)
 	defer pathDB.Close()
-	trustDB, err := cfg.TrustDB.New()
-	if err != nil {
-		log.Crit("Unable to initialize trustDB", "err", err)
-		return 1
-	}
-	trustDB = trustdb.WithMetrics(string(cfg.TrustDB.Backend()), trustDB)
-	defer trustDB.Close()
+
 	topo := itopo.Get()
-	trustConf := trust.Config{
-		MustHaveLocalChain: true,
-		ServiceType:        proto.ServiceType_ps,
-		TopoProvider:       itopo.Provider(),
-	}
-	trustStore := trust.NewStore(trustDB, topo.IA(), trustConf, log.Root())
-	err = trustStore.LoadAuthoritativeCrypto(filepath.Join(cfg.General.ConfigDir, "certs"))
-	if err != nil {
-		log.Crit("Unable to load local crypto", "err", err)
-		return 1
-	}
 	if !topo.Exists(addr.SvcPS, cfg.General.ID) {
 		log.Crit("Unable to find topo address")
 		return 1
@@ -135,7 +118,6 @@ func realMain() int {
 			KeyFile:  cfg.QUIC.KeyFile,
 		},
 		SVCResolutionFraction: cfg.QUIC.ResolutionFraction,
-		TrustStore:            trustStore,
 		SVCRouter:             messenger.NewSVCRouter(itopo.Provider()),
 	}
 	msger, err := nc.Messenger()
@@ -144,15 +126,49 @@ func realMain() int {
 		return 1
 	}
 	defer msger.CloseServer()
-	msger.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler(false))
+
+	trustDB, err := cfg.TrustDB.New()
+	if err != nil {
+		log.Crit("Error initializing trust database", "err", err)
+		return 1
+	}
+	defer trustDB.Close()
+	inserter := trust.ForwardingInserter{
+		BaseInserter: trust.BaseInserter{DB: trustDB},
+		Router:       trust.LocalRouter{IA: topo.IA()},
+		RPC:          trust.DefaultRPC{Msgr: msger},
+	}
+	provider := trust.Provider{
+		DB:       trustDB,
+		Recurser: trust.LocalOnlyRecurser{},
+		Resolver: trust.DefaultResolver{
+			DB:       trustDB,
+			Inserter: inserter,
+			RPC:      trust.DefaultRPC{Msgr: msger},
+		},
+		Router: trust.LocalRouter{IA: topo.IA()},
+	}
+	trustStore := trust.Store{
+		Inspector:      trust.DefaultInspector{Provider: provider},
+		CryptoProvider: provider,
+		Inserter:       inserter,
+		DB:             trustDB,
+	}
+	certsDir := filepath.Join(cfg.General.ConfigDir, "certs")
+	if err = trustStore.LoadCryptoMaterial(context.Background(), certsDir); err != nil {
+		log.Crit("Error loading crypto material", "err", err)
+		return 1
+	}
+
+	msger.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler())
 	// TODO(lukedirtwalker): with the new CP-PKI design the PS should no longer need to handle TRC
 	// and cert requests.
-	msger.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler(false))
+	msger.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler())
 	args := handlers.HandlerArgs{
 		PathDB:          pathDB,
 		RevCache:        revCache,
 		ASInspector:     trustStore,
-		VerifierFactory: trustStore,
+		VerifierFactory: verificationFactory{Provider: trustStore},
 		QueryInterval:   cfg.PS.QueryInterval.Duration,
 		IA:              topo.IA(),
 		TopoProvider:    itopo.Provider(),
@@ -200,7 +216,7 @@ func realMain() int {
 type periodicTasks struct {
 	args          handlers.HandlerArgs
 	msger         infra.Messenger
-	trustDB       trustdb.TrustDB
+	trustDB       trust.DB
 	mtx           sync.Mutex
 	running       bool
 	segSyncers    []*periodic.Runner
@@ -226,11 +242,12 @@ func (t *periodicTasks) Start() error {
 	}
 	t.pathDBCleaner = periodic.Start(pathdb.NewCleaner(t.args.PathDB, "ps_segments"),
 		300*time.Second, 295*time.Second)
-	t.cryptosyncer = periodic.Start(&cryptosyncer.Syncer{
-		DB:    t.trustDB,
-		Msger: t.msger,
-		IA:    t.args.IA,
-	}, cfg.PS.CryptoSyncInterval.Duration, cfg.PS.CryptoSyncInterval.Duration)
+	// TODO(roosd): Re-enable
+	// t.cryptosyncer = periodic.Start(&cryptosyncer.Syncer{
+	// 	DB:    t.trustDB,
+	// 	Msger: t.msger,
+	// 	IA:    t.args.IA,
+	// }, cfg.PS.CryptoSyncInterval.Duration, cfg.PS.CryptoSyncInterval.Duration)
 	t.rcCleaner = periodic.Start(revcache.NewCleaner(t.args.RevCache, "ps_revocation"),
 		10*time.Second, 10*time.Second)
 	return nil
@@ -253,6 +270,18 @@ func (t *periodicTasks) Kill() {
 	t.running = false
 }
 
+type verificationFactory struct {
+	Provider trust.CryptoProvider
+}
+
+func (v verificationFactory) NewSigner(common.RawBytes, infra.SignerMeta) (infra.Signer, error) {
+	return nil, serrors.New("signer generation not supported")
+}
+
+func (v verificationFactory) NewVerifier() infra.Verifier {
+	return trust.NewVerifier(v.Provider)
+}
+
 func setupBasic() error {
 	if _, err := toml.DecodeFile(env.ConfigFile(), &cfg); err != nil {
 		return err
diff --git a/go/sciond/BUILD.bazel b/go/sciond/BUILD.bazel
index 81a59859f..8a1c67487 100644
--- a/go/sciond/BUILD.bazel
+++ b/go/sciond/BUILD.bazel
@@ -12,18 +12,20 @@ go_library(
         "//go/lib/discovery:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/fatal:go_default_library",
+        "//go/lib/infra:go_default_library",
         "//go/lib/infra/infraenv:go_default_library",
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/modules/idiscovery:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
         "//go/lib/infra/modules/segfetcher:go_default_library",
-        "//go/lib/infra/modules/trust:go_default_library",
+        "//go/lib/infra/modules/trust/v2:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/pathdb:go_default_library",
         "//go/lib/pathstorage:go_default_library",
         "//go/lib/periodic:go_default_library",
         "//go/lib/prom:go_default_library",
         "//go/lib/revcache:go_default_library",
+        "//go/lib/serrors:go_default_library",
         "//go/lib/topology:go_default_library",
         "//go/proto:go_default_library",
         "//go/sciond/internal/config:go_default_library",
diff --git a/go/sciond/internal/fetcher/fetcher.go b/go/sciond/internal/fetcher/fetcher.go
index 9dfe608c6..137400c47 100644
--- a/go/sciond/internal/fetcher/fetcher.go
+++ b/go/sciond/internal/fetcher/fetcher.go
@@ -59,8 +59,9 @@ type Fetcher struct {
 	segfetcher      *segfetcher.Fetcher
 }
 
-func NewFetcher(messenger infra.Messenger, pathDB pathdb.PathDB, trustStore TrustStore,
-	revCache revcache.RevCache, cfg config.SDConfig, topoProvider topology.Provider) *Fetcher {
+func NewFetcher(messenger infra.Messenger, pathDB pathdb.PathDB, inspector infra.ASInspector,
+	verificationFactory infra.VerificationFactory, revCache revcache.RevCache, cfg config.SDConfig,
+	topoProvider topology.Provider) *Fetcher {
 
 	localIA := topoProvider.Get().IA()
 	return &Fetcher{
@@ -71,13 +72,13 @@ func NewFetcher(messenger infra.Messenger, pathDB pathdb.PathDB, trustStore Trus
 		segfetcher: segfetcher.FetcherConfig{
 			QueryInterval:       cfg.QueryInterval.Duration,
 			LocalIA:             localIA,
-			ASInspector:         trustStore,
-			VerificationFactory: trustStore,
+			ASInspector:         inspector,
+			VerificationFactory: verificationFactory,
 			PathDB:              pathDB,
 			RevCache:            revCache,
 			RequestAPI:          messenger,
 			DstProvider:         &dstProvider{IA: localIA},
-			Splitter:            NewRequestSplitter(localIA, trustStore),
+			Splitter:            NewRequestSplitter(localIA, inspector),
 			SciondMode:          true,
 			MetricsNamespace:    metrics.Namespace,
 			LocalInfo:           neverLocal{},
diff --git a/go/sciond/main.go b/go/sciond/main.go
index e0f00db61..47665973c 100644
--- a/go/sciond/main.go
+++ b/go/sciond/main.go
@@ -32,18 +32,20 @@ import (
 	"github.com/scionproto/scion/go/lib/discovery"
 	"github.com/scionproto/scion/go/lib/env"
 	"github.com/scionproto/scion/go/lib/fatal"
+	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/infraenv"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/idiscovery"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/pathstorage"
 	"github.com/scionproto/scion/go/lib/periodic"
 	"github.com/scionproto/scion/go/lib/prom"
 	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/proto"
 	"github.com/scionproto/scion/go/sciond/internal/config"
@@ -97,19 +99,6 @@ func realMain() int {
 	}
 	defer pathDB.Close()
 	defer revCache.Close()
-	trustDB, err := cfg.TrustDB.New()
-	if err != nil {
-		log.Crit("Unable to initialize trustDB", "err", err)
-		return 1
-	}
-	defer trustDB.Close()
-	trustConf := trust.Config{TopoProvider: itopo.Provider()}
-	trustStore := trust.NewStore(trustDB, itopo.Get().IA(), trustConf, log.Root())
-	err = trustStore.LoadAuthoritativeTRC(filepath.Join(cfg.General.ConfigDir, "certs"))
-	if err != nil {
-		log.Crit("Unable to load local TRC", "err", err)
-		return 1
-	}
 	tracer, trCloser, err := cfg.Tracing.NewTracer(cfg.General.ID)
 	if err != nil {
 		log.Crit("Unable to create tracer", "err", err)
@@ -135,7 +124,6 @@ func realMain() int {
 			KeyFile:  cfg.QUIC.KeyFile,
 		},
 		SVCResolutionFraction: cfg.QUIC.ResolutionFraction,
-		TrustStore:            trustStore,
 		SVCRouter:             messenger.NewSVCRouter(itopo.Provider()),
 	}
 	msger, err := nc.Messenger()
@@ -143,6 +131,40 @@ func realMain() int {
 		log.Crit(infraenv.ErrAppUnableToInitMessenger.Error(), "err", err)
 		return 1
 	}
+	defer msger.CloseServer()
+
+	trustDB, err := cfg.TrustDB.New()
+	if err != nil {
+		log.Crit("Error initializing trust database", "err", err)
+		return 1
+	}
+	defer trustDB.Close()
+	inserter := trust.DefaultInserter{
+		BaseInserter: trust.BaseInserter{DB: trustDB},
+	}
+	provider := trust.Provider{
+		DB:       trustDB,
+		Recurser: trust.LocalOnlyRecurser{},
+		Resolver: trust.DefaultResolver{
+			DB:       trustDB,
+			Inserter: inserter,
+			RPC:      trust.DefaultRPC{Msgr: msger},
+		},
+		Router: trust.LocalRouter{IA: itopo.Get().IA()},
+	}
+	trustStore := trust.Store{
+		Inspector:      trust.DefaultInspector{Provider: provider},
+		CryptoProvider: provider,
+		Inserter:       inserter,
+		DB:             trustDB,
+	}
+	certsDir := filepath.Join(cfg.General.ConfigDir, "certs")
+	err = trustStore.LoadCryptoMaterial(context.Background(), certsDir)
+	if err != nil {
+		log.Crit("Error loading crypto material", "err", err)
+		return 1
+	}
+
 	// Route messages to their correct handlers
 	handlers := servers.HandlerMap{
 		proto.SCIONDMsg_Which_pathReq: &servers.PathRequestHandler{
@@ -150,6 +172,7 @@ func realMain() int {
 				msger,
 				pathDB,
 				trustStore,
+				verificationFactory{Provider: trustStore},
 				revCache,
 				cfg.SD,
 				itopo.Provider(),
@@ -162,7 +185,7 @@ func realMain() int {
 		proto.SCIONDMsg_Which_serviceInfoRequest: &servers.SVCInfoRequestHandler{},
 		proto.SCIONDMsg_Which_revNotification: &servers.RevNotificationHandler{
 			RevCache:         revCache,
-			VerifierFactory:  trustStore,
+			VerifierFactory:  verificationFactory{Provider: trustStore},
 			NextQueryCleaner: segfetcher.NextQueryCleaner{PathDB: pathDB},
 		},
 	}
@@ -190,6 +213,18 @@ func realMain() int {
 	}
 }
 
+type verificationFactory struct {
+	Provider trust.CryptoProvider
+}
+
+func (v verificationFactory) NewSigner(common.RawBytes, infra.SignerMeta) (infra.Signer, error) {
+	return nil, serrors.New("signer generation not supported")
+}
+
+func (v verificationFactory) NewVerifier() infra.Verifier {
+	return trust.NewVerifier(v.Provider)
+}
+
 func setupBasic() error {
 	if _, err := toml.DecodeFile(env.ConfigFile(), &cfg); err != nil {
 		return err
diff --git a/go/tools/scion-custpk-load/BUILD.bazel b/go/tools/scion-custpk-load/BUILD.bazel
index b9d202021..4a97a3057 100644
--- a/go/tools/scion-custpk-load/BUILD.bazel
+++ b/go/tools/scion-custpk-load/BUILD.bazel
@@ -17,6 +17,7 @@ go_library(
         "//go/lib/infra/modules/trust/trustdb:go_default_library",
         "//go/lib/keyconf:go_default_library",
         "//go/lib/scrypto:go_default_library",
+        "//go/lib/serrors:go_default_library",
         "//go/lib/truststorage:go_default_library",
         "@com_github_burntsushi_toml//:go_default_library",
     ],
diff --git a/go/tools/scion-custpk-load/main.go b/go/tools/scion-custpk-load/main.go
index 7c892f841..68b55b338 100644
--- a/go/tools/scion-custpk-load/main.go
+++ b/go/tools/scion-custpk-load/main.go
@@ -26,6 +26,7 @@ import (
 	"github.com/scionproto/scion/go/lib/config"
 	"github.com/scionproto/scion/go/lib/env"
 	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb"
+	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/truststorage"
 )
 
@@ -106,10 +107,11 @@ func loadConfig() error {
 	if err != nil {
 		return common.NewBasicError("Unable to validate config", err)
 	}
-	if trustDB, err = cfg.TrustDB.New(); err != nil {
-		return common.NewBasicError("Failed to init the database", err)
-	}
-	return nil
+	// TODO(roosd): uncomment
+	// if trustDB, err = cfg.TrustDB.New(); err != nil {
+	// 	return common.NewBasicError("Failed to init the database", err)
+	// }
+	return serrors.New("not implemented")
 }
 
 func printSummary(files []string, loadedCusts []*CustKeyMeta) {
diff --git a/python/topology/cert.py b/python/topology/cert.py
index c6b389a33..4dfeeffbc 100755
--- a/python/topology/cert.py
+++ b/python/topology/cert.py
@@ -16,6 +16,8 @@
 :mod:`cert` --- SCION topology certificate generator
 =============================================
 """
+import base64
+import os
 from collections import defaultdict
 
 from plumbum import local
@@ -38,13 +40,23 @@ class CertGenerator(object):
         self.core_count = defaultdict(int)
 
     def generate(self, topo_dicts):
-        self.pki('tmpl', 'topo', self.args.topo_config, '-d', self.args.output_dir)
-        self.pki('keys', 'gen', '*', '-d', self.args.output_dir)
-        self.pki('trc', 'gen', '*', '-d', self.args.output_dir)
-        self.pki('certs', 'gen', '*', '-d', self.args.output_dir)
-        self.pki('certs', 'customers', '*', '-d', self.args.output_dir)
+        self.pki('v2', 'tmpl', 'topo', self.args.topo_config, '-d', self.args.output_dir)
+        self.pki('v2', 'keys', 'private', '*', '-d', self.args.output_dir)
+        self.pki('v2', 'keys', 'master', '*', '-d', self.args.output_dir)
+        self.pki('v2', 'trcs', 'gen', '*', '-d', self.args.output_dir)
+        self.pki('v2', 'certs', 'issuer', '*', '-d', self.args.output_dir)
+        self.pki('v2', 'certs', 'chain', '*', '-d', self.args.output_dir)
+        self._master_keys(topo_dicts)
         self._copy_files(topo_dicts)
 
+    def _master_keys(self, topo_dicts):
+        for topo_id, as_topo in topo_dicts.items():
+            base = topo_id.base_dir(self.args.output_dir)
+            with open(os.path.join(base, 'keys', 'master0.key'), 'w') as f:
+                f.write(base64.b64encode(os.urandom(16)).decode())
+            with open(os.path.join(base, 'keys', 'master1.key'), 'w') as f:
+                f.write(base64.b64encode(os.urandom(16)).decode())
+
     def _copy_files(self, topo_dicts):
         cp = local['cp']
         # Copy the certs and key dir for all elements.
@@ -54,7 +66,7 @@ class CertGenerator(object):
             as_dir = elem_dir.dirname
             cp('-r', as_dir / 'certs', elem_dir / 'certs')
             cp('-r', as_dir / 'keys', elem_dir / 'keys')
-            cp(as_dir.dirname / 'trcs' // '*.trc', elem_dir / 'certs')
+            cp(as_dir.dirname.dirname // '*/trcs/*.trc', elem_dir / 'certs')
         # Copy the customers dir for all certificate servers.
         for topo_id, as_topo in topo_dicts.items():
             as_dir = local.path(topo_id.base_dir(self.args.output_dir))


Test patch:
diff --git a/go/cert_srv/internal/config/state_test.go b/go/cert_srv/internal/config/state_test.go
deleted file mode 100644
index ac9864fd0..000000000
--- a/go/cert_srv/internal/config/state_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 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 config
-
-import (
-	"testing"
-
-	. "github.com/smartystreets/goconvey/convey"
-
-	"github.com/scionproto/scion/go/lib/keyconf"
-	"github.com/scionproto/scion/go/lib/scrypto"
-)
-
-func TestLoadState(t *testing.T) {
-	mstr0, _ := keyconf.LoadKey("testdata/keys/master0.key", keyconf.RawKey)
-	mstr1, _ := keyconf.LoadKey("testdata/keys/master1.key", keyconf.RawKey)
-	dcrpt, _ := keyconf.LoadKey("testdata/keys/as-decrypt.key",
-		scrypto.Curve25519xSalsa20Poly1305)
-	asSig, _ := keyconf.LoadKey("testdata/keys/as-sig.seed", scrypto.Ed25519)
-	issSig, _ := keyconf.LoadKey("testdata/keys/core-sig.seed", scrypto.Ed25519)
-	online, _ := keyconf.LoadKey("testdata/keys/online-root.seed", scrypto.Ed25519)
-	Convey("Load core state", t, func() {
-		state, err := LoadState("testdata", true, nil, nil)
-		SoMsg("err", err, ShouldBeNil)
-		SoMsg("Master0", state.keyConf.Master.Key0, ShouldResemble, mstr0)
-		SoMsg("Master1", state.keyConf.Master.Key1, ShouldResemble, mstr1)
-		SoMsg("Decrypt", state.keyConf.DecryptKey, ShouldResemble, dcrpt)
-		SoMsg("AS-Sign", state.keyConf.SignKey, ShouldResemble, asSig)
-		SoMsg("Issuer-Sign", state.keyConf.IssSigKey, ShouldResemble, issSig)
-		SoMsg("Online", state.keyConf.OnRootKey, ShouldResemble, online)
-		SoMsg("Offline", state.keyConf.OffRootKey, ShouldBeZeroValue)
-	})
-
-	Convey("Load non-core state", t, func() {
-		state, err := LoadState("testdata", false, nil, nil)
-		SoMsg("err", err, ShouldBeNil)
-		SoMsg("Master0", state.keyConf.Master.Key0, ShouldResemble, mstr0)
-		SoMsg("Master1", state.keyConf.Master.Key1, ShouldResemble, mstr1)
-		SoMsg("Decrypt", state.keyConf.DecryptKey, ShouldResemble, dcrpt)
-		SoMsg("AS-Sign", state.keyConf.SignKey, ShouldResemble, asSig)
-		SoMsg("Issuer-Sign", state.keyConf.IssSigKey, ShouldBeZeroValue)
-		SoMsg("Online", state.keyConf.OnRootKey, ShouldBeZeroValue)
-		SoMsg("Offline", state.keyConf.OffRootKey, ShouldBeZeroValue)
-	})
-}
diff --git a/go/cert_srv/internal/reiss/corepush_test.go b/go/cert_srv/internal/reiss/corepush_test.go
deleted file mode 100644
index e03c77388..000000000
--- a/go/cert_srv/internal/reiss/corepush_test.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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 reiss
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/golang/mock/gomock"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/cert_mgmt"
-	"github.com/scionproto/scion/go/lib/infra/mock_infra"
-	"github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb/mock_trustdb"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
-	"github.com/scionproto/scion/go/lib/scrypto/trc"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/xtest"
-	"github.com/scionproto/scion/go/lib/xtest/matchers"
-)
-
-var (
-	localIA  = xtest.MustParseIA("1-ff00:0:311")
-	localISD = localIA.I
-
-	core1_110 = xtest.MustParseIA("1-ff00:0:110")
-	core1_130 = xtest.MustParseIA("1-ff00:0:130")
-	core1_120 = xtest.MustParseIA("1-ff00:0:120")
-
-	trcISD1 = &trc.TRC{
-		CoreASes: trc.CoreASMap{
-			core1_110: nil,
-			core1_120: nil,
-			core1_130: nil,
-		},
-	}
-
-	chain    *cert.Chain
-	rawChain common.RawBytes
-	chainMsg *cert_mgmt.Chain
-
-	emptyChainMsg = &cert_mgmt.Chain{RawChain: nil}
-)
-
-func setup(t *testing.T) (*gomock.Controller, *mock_infra.MockMessenger, periodic.Task) {
-	ctrl := gomock.NewController(t)
-	trustDB := mock_trustdb.NewMockTrustDB(ctrl)
-	msger := mock_infra.NewMockMessenger(ctrl)
-	pusher := &CorePusher{
-		LocalIA: localIA,
-		TrustDB: trustDB,
-		Msger:   msger,
-	}
-	var err error
-	chain, err = cert.ChainFromFile("testdata/ISD1-ASff00_0_311-V1.crt", false)
-	xtest.FailOnErr(t, err)
-	rawChain, err = chain.Compress()
-	xtest.FailOnErr(t, err)
-	chainMsg = &cert_mgmt.Chain{RawChain: rawChain}
-	SleepAfterFailure = 0
-
-	trustDB.EXPECT().GetTRCMaxVersion(gomock.Any(), gomock.Eq(localISD)).Return(trcISD1, nil)
-	trustDB.EXPECT().GetChainMaxVersion(gomock.Any(), gomock.Eq(localIA)).Return(chain, nil)
-
-	return ctrl, msger, pusher
-}
-
-func TestNonExistingChainsArePushed(t *testing.T) {
-	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
-	defer cancelF()
-	ctrl, msger, pusher := setup(t)
-	defer ctrl.Finish()
-
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_110), gomock.Any()).Return(
-		emptyChainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_120), gomock.Any()).Return(
-		emptyChainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_130), gomock.Any()).Return(
-		emptyChainMsg, nil,
-	)
-	msger.EXPECT().SendCertChain(
-		gomock.Any(), matchesChain(rawChain), matchers.IsSnetAddrWithIA(core1_110), gomock.Any())
-	msger.EXPECT().SendCertChain(
-		gomock.Any(), matchesChain(rawChain), matchers.IsSnetAddrWithIA(core1_120), gomock.Any())
-	msger.EXPECT().SendCertChain(
-		gomock.Any(), matchesChain(rawChain), matchers.IsSnetAddrWithIA(core1_130), gomock.Any())
-	pusher.Run(ctx)
-}
-
-func TestExistingChainsAreNotPushed(t *testing.T) {
-	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
-	defer cancelF()
-	ctrl, msger, pusher := setup(t)
-	defer ctrl.Finish()
-
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_110), gomock.Any()).Return(
-		chainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_120), gomock.Any()).Return(
-		chainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_130), gomock.Any()).Return(
-		emptyChainMsg, nil,
-	)
-	msger.EXPECT().SendCertChain(
-		gomock.Any(), matchesChain(rawChain), matchers.IsSnetAddrWithIA(core1_130), gomock.Any())
-	pusher.Run(ctx)
-}
-
-func TestErrDuringSendIsRetried(t *testing.T) {
-	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
-	defer cancelF()
-	ctrl, msger, pusher := setup(t)
-	defer ctrl.Finish()
-
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_110), gomock.Any()).Return(
-		chainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_120), gomock.Any()).Return(
-		chainMsg, nil,
-	)
-	msger.EXPECT().GetCertChain(
-		gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_130), gomock.Any()).Return(
-		emptyChainMsg, nil,
-	)
-	gomock.InOrder(
-		msger.EXPECT().SendCertChain(
-			gomock.Any(), matchesChain(rawChain), matchers.IsSnetAddrWithIA(core1_130),
-			gomock.Any()).Return(serrors.New("test error")),
-		msger.EXPECT().GetCertChain(
-			gomock.Any(), gomock.Any(), matchers.IsSnetAddrWithIA(core1_130), gomock.Any()).Return(
-			emptyChainMsg, nil,
-		),
-		msger.EXPECT().SendCertChain(
-			gomock.Any(), matchesChain(rawChain),
-			matchers.IsSnetAddrWithIA(core1_130), gomock.Any()),
-	)
-	pusher.Run(ctx)
-}
-
-var _ gomock.Matcher = (*chainMsgMatcher)(nil)
-
-type chainMsgMatcher struct {
-	rawChain common.RawBytes
-}
-
-func matchesChain(rawChain common.RawBytes) *chainMsgMatcher {
-	return &chainMsgMatcher{
-		rawChain: rawChain,
-	}
-}
-
-func (m *chainMsgMatcher) Matches(x interface{}) bool {
-	msg, ok := x.(*cert_mgmt.Chain)
-	if !ok {
-		return false
-	}
-	return bytes.Equal(m.rawChain, msg.RawChain)
-}
-
-func (m *chainMsgMatcher) String() string {
-	return fmt.Sprintf("Chain msg with raw: %s", m.rawChain)
-}
diff --git a/go/cert_srv/internal/reiss/testdata/ISD1-ASff00_0_311-V1.crt b/go/cert_srv/internal/reiss/testdata/ISD1-ASff00_0_311-V1.crt
deleted file mode 100644
index b982d9ed3..000000000
--- a/go/cert_srv/internal/reiss/testdata/ISD1-ASff00_0_311-V1.crt
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-    "0": {
-    "SignAlgorithm": "ed25519",
-    "SubjectSignKey": "5YYo/Djor8KoUPbcG89m0sOXbhaxU/wserVf7X4w0W4=",
-    "Version": 1,
-    "EncAlgorithm": "curve25519xsalsa20poly1305",
-    "SubjectEncKey": "nP1HkZwkW8ujqeEO82Rb9cN6AVqFPO1UIiypdZU+dHI=",
-    "TRCVersion": 2,
-    "ExpirationTime": 1539868933,
-    "Signature": "36dhobVsPBt6UlMCZtmYHoKJbuS3MbZNvu24nA+kt780bf4ZenIreuvnxphIxuI327cBoeDsB+Tg1EvSPnwEBg==",
-    "Issuer": "1-ff00:0:110",
-    "CanIssue": false,
-    "Subject": "1-ff00:0:311",
-    "IssuingTime": 1508332933,
-    "Comment": "AS Certificate\u2602\u2602\u2602\u2602"
-    },
-    "1": {
-    "SignAlgorithm": "ed25519",
-    "SubjectSignKey": "kqh9WJfVH10/apH278var5ec3AYIU5LjdS1n7SP5+p8=",
-    "Version": 1,
-    "EncAlgorithm": "curve25519xsalsa20poly1305",
-    "SubjectEncKey": "MxxFIP+KlhIyqxsv6B0Um72D+NPoLKGuBPNt8b14/RI=",
-    "TRCVersion": 2,
-    "ExpirationTime": 1539868933,
-    "Signature": "CbjEsqIe4LW7kjMsyMBim4RdRn0YwM4kCjED+1Lbja4o3lwQVHqxVzQ8Rx0CsmHdsm4mwPoNg++KKUlUxrRmCg==",
-    "Issuer": "1-ff00:0:110",
-    "CanIssue": true,
-    "Subject": "1-ff00:0:110",
-    "IssuingTime": 1508332933,
-    "Comment": "Core AS Certificate"
-    }
-}
diff --git a/go/lib/infra/modules/trust/v2/export_test.go b/go/lib/infra/modules/trust/v2/export_test.go
deleted file mode 100644
index ed24577f7..000000000
--- a/go/lib/infra/modules/trust/v2/export_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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
-
-import "github.com/scionproto/scion/go/lib/infra"
-
-var (
-	// NewChainPushHandler allows instantiating the private chain push handler for black-box
-	// testing.
-	NewChainPushHandler = newTestChainPushHandler
-	// NewTRCPushHandler allows instantiating the private TRC push handler for black-box
-	// testing.
-	NewTRCPushHandler = newTestTRCPushHandler
-	// NewChainReqHandler allows instantiating the private certificate chain
-	// request handler for black-box testing.
-	NewChainReqHandler = newTestChainReqHandler
-	// NewTRCReqHandler allows instantiating the private trc request handler for
-	// black-box testing.
-	NewTRCReqHandler = newTestTRCReqResolver
-)
-
-// newTestChainReqHandler returns a new resolver for testing.
-func newTestChainReqHandler(request *infra.Request, provider CryptoProvider) *chainReqHandler {
-	return &chainReqHandler{
-		request:  request,
-		provider: provider,
-	}
-}
-
-// newTestResolver returns a new resolver for testing.
-func newTestTRCReqResolver(request *infra.Request, provider CryptoProvider) *trcReqHandler {
-	return &trcReqHandler{
-		request:  request,
-		provider: provider,
-	}
-}
-
-// newChainPushHandler returns a new chain push handler for testing.
-func newTestChainPushHandler(request *infra.Request, provider CryptoProvider,
-	inserter Inserter) *chainPushHandler {
-
-	return &chainPushHandler{
-		request:  request,
-		provider: provider,
-		inserter: inserter,
-	}
-}
-
-// newTRCPushHandler returns a new TRC push handler for testing.
-func newTestTRCPushHandler(request *infra.Request, provider CryptoProvider,
-	inserter Inserter) *trcPushHandler {
-
-	return &trcPushHandler{
-		request:  request,
-		provider: provider,
-		inserter: inserter,
-	}
-}
diff --git a/go/lib/infra/modules/trust/v2/handlers_test.go b/go/lib/infra/modules/trust/v2/handlers_test.go
index 99fee4e75..c94f28076 100644
--- a/go/lib/infra/modules/trust/v2/handlers_test.go
+++ b/go/lib/infra/modules/trust/v2/handlers_test.go
@@ -150,11 +150,10 @@ func TestChainReqHandler(t *testing.T) {
 			t.Parallel()
 			ctrl := gomock.NewController(t)
 			defer ctrl.Finish()
-			handler := trust.NewChainReqHandler(
-				test.Request(ctrl),
-				test.Provider(ctrl),
-			)
-			result := handler.Handle()
+			store := trust.Store{
+				CryptoProvider: test.Provider(ctrl),
+			}
+			result := store.NewChainReqHandler().Handle(test.Request(ctrl))
 			assert.Equal(t, test.ExpectedResult, result)
 		})
 	}
@@ -277,11 +276,10 @@ func TestTRCReqHandler(t *testing.T) {
 			t.Parallel()
 			ctrl := gomock.NewController(t)
 			defer ctrl.Finish()
-			handler := trust.NewTRCReqHandler(
-				test.Request(ctrl),
-				test.Provider(ctrl),
-			)
-			result := handler.Handle()
+			store := trust.Store{
+				CryptoProvider: test.Provider(ctrl),
+			}
+			result := store.NewTRCReqHandler().Handle(test.Request(ctrl))
 			assert.Equal(t, test.ExpectedResult, result)
 		})
 	}
@@ -451,20 +449,18 @@ func TestChainPushHandler(t *testing.T) {
 		},
 	}
 
-	for _, test := range testCases {
-		tc := test
-		t.Run(tc.Name, func(t *testing.T) {
+	for _, tc := range testCases {
+		test := tc
+		t.Run(test.Name, func(t *testing.T) {
 			t.Parallel()
 			ctrl := gomock.NewController(t)
 			defer ctrl.Finish()
 
-			chainPushHandler := trust.NewChainPushHandler(
-				tc.Request(ctrl),
-				nil,
-				tc.Inserter(ctrl),
-			)
-			result := chainPushHandler.Handle()
-			assert.Equal(t, tc.ExpectedResult, result)
+			store := trust.Store{
+				Inserter: test.Inserter(ctrl),
+			}
+			result := store.NewChainPushHandler().Handle(test.Request(ctrl))
+			assert.Equal(t, test.ExpectedResult, result)
 		})
 	}
 }
@@ -633,20 +629,18 @@ func TestTRCPushHandler(t *testing.T) {
 		},
 	}
 
-	for _, test := range testCases {
-		tc := test
-		t.Run(tc.Name, func(t *testing.T) {
+	for _, tc := range testCases {
+		test := tc
+		t.Run(test.Name, func(t *testing.T) {
 			t.Parallel()
 			ctrl := gomock.NewController(t)
 			defer ctrl.Finish()
 
-			trcPushHandler := trust.NewTRCPushHandler(
-				tc.Request(ctrl),
-				nil,
-				tc.Inserter(ctrl),
-			)
-			result := trcPushHandler.Handle()
-			assert.Equal(t, tc.ExpectedResult, result)
+			store := trust.Store{
+				Inserter: test.Inserter(ctrl),
+			}
+			result := store.NewTRCPushHandler().Handle(test.Request(ctrl))
+			assert.Equal(t, test.ExpectedResult, result)
 		})
 	}
 }
diff --git a/go/lib/infra/modules/trust/v2/store_test.go b/go/lib/infra/modules/trust/v2/store_test.go
new file mode 100644
index 000000000..e3f7c890c
--- /dev/null
+++ b/go/lib/infra/modules/trust/v2/store_test.go
@@ -0,0 +1,145 @@
+// 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"
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/infra"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2"
+	"github.com/scionproto/scion/go/lib/infra/modules/trust/v2/trustdbsqlite"
+	"github.com/scionproto/scion/go/lib/scrypto"
+	"github.com/scionproto/scion/go/lib/xtest"
+)
+
+func TestStoreLoadCryptoMaterial(t *testing.T) {
+	tests := map[string]struct {
+		Prepare      func(t *testing.T, scratch string)
+		ErrAssertion assert.ErrorAssertionFunc
+	}{
+		"valid": {
+			Prepare: func(t *testing.T, scratch string) {
+				collectTRCs(t, tmpDir, scratch)
+				collectChains(t, tmpDir, scratch)
+			},
+			ErrAssertion: assert.NoError,
+		},
+		"unreadable TRC": {
+			Prepare: func(t *testing.T, scratch string) {
+				collectChains(t, tmpDir, scratch)
+				err := ioutil.WriteFile(filepath.Join(scratch, "ISD1-V1.trc"), []byte("no"), 0x000)
+				require.NoError(t, err)
+			},
+			ErrAssertion: assert.Error,
+		},
+		"garbage TRC": {
+			Prepare: func(t *testing.T, scratch string) {
+				collectChains(t, tmpDir, scratch)
+				err := ioutil.WriteFile(filepath.Join(scratch, "ISD1-V1.trc"), []byte("no"), 0x777)
+				require.NoError(t, err)
+			},
+			ErrAssertion: assert.Error,
+		},
+		"unreadable chain": {
+			Prepare: func(t *testing.T, scratch string) {
+				collectTRCs(t, tmpDir, scratch)
+				err := ioutil.WriteFile(filepath.Join(scratch, "ISD1-AS1-V1.crt"),
+					[]byte("no"), 0x000)
+				require.NoError(t, err)
+			},
+			ErrAssertion: assert.Error,
+		},
+		"garbage chain": {
+			Prepare: func(t *testing.T, scratch string) {
+				collectTRCs(t, tmpDir, scratch)
+				err := ioutil.WriteFile(filepath.Join(scratch, "ISD1-AS1-V1.crt"),
+					[]byte("no"), 0x777)
+				require.NoError(t, err)
+			},
+			ErrAssertion: assert.Error,
+		},
+	}
+	for n, tc := range tests {
+		name, test := n, tc
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+			scratch, clean := xtest.MustTempDir("", "trust-load-crypto")
+			defer clean()
+			db, err := trustdbsqlite.New(":memory:")
+			require.NoError(t, err)
+			store := trust.Store{
+				DB: db,
+				CryptoProvider: trust.Provider{
+					DB: db,
+				},
+			}
+			test.Prepare(t, scratch)
+			err = store.LoadCryptoMaterial(context.Background(), scratch)
+			test.ErrAssertion(t, err)
+			if err != nil {
+				return
+			}
+			trcOpts := infra.TRCOpts{AllowInactive: true}
+			for v := scrypto.Version(1); v < 5; v++ {
+				id := trust.TRCID{ISD: 1, Version: v}
+				raw, err := store.GetRawTRC(context.Background(), id, trcOpts)
+				assert.NoError(t, err)
+				assert.Equal(t, loadTRC(t, TRCDesc{ISD: 1, Version: v}).Raw, raw)
+			}
+			chainOpts := infra.ChainOpts{AllowInactive: true}
+			for _, ia := range []addr.IA{ia110, ia120, ia122, ia130, ia210} {
+				id := trust.ChainID{IA: ia, Version: 1}
+				raw, err := store.GetRawChain(context.Background(), id, chainOpts)
+				assert.NoError(t, err)
+				assert.Equal(t, loadChain(t, ChainDesc{IA: ia, Version: 1}).Raw, raw)
+			}
+		})
+	}
+}
+
+func collectTRCs(t *testing.T, origDir, outDir string) {
+	t.Helper()
+	trcs, err := filepath.Glob(filepath.Join(origDir, "ISD*/trcs/*.trc"))
+	require.NoError(t, err, help)
+	require.Greater(t, len(trcs), 0)
+	for _, trc := range trcs {
+		raw, err := ioutil.ReadFile(trc)
+		require.NoError(t, err)
+		_, file := filepath.Split(trc)
+		err = ioutil.WriteFile(filepath.Join(outDir, file), raw, 0x777)
+		require.NoError(t, err)
+	}
+}
+
+func collectChains(t *testing.T, origDir, outDir string) {
+	t.Helper()
+	chains, err := filepath.Glob(filepath.Join(origDir, "ISD*/AS*/certs/*.crt"))
+	require.NoError(t, err, help)
+	require.Greater(t, len(chains), 0)
+	for _, chain := range chains {
+		raw, err := ioutil.ReadFile(chain)
+		require.NoError(t, err)
+		_, file := filepath.Split(chain)
+		err = ioutil.WriteFile(filepath.Join(outDir, file), raw, 0x777)
+		require.NoError(t, err)
+	}
+}