diff --git a/README.md b/README.md index fe988b17c384224673767008b98d3637f131689a..f20c8119d3a40cbe3a231e02850f517862ca9f89 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ Peers: Mastermode: true # (Type: etsi specific) Sets the QuantumModule in MasterMode. Keys are requested via GetKey and synced with peer KMS. LocalSAEID: "localSAEID" # the related ID of the local SAE TargetSAEID: "targetSAEID" # the related ID of the target SAE + KeyFetchInterval: 10 # interval in seconds for how often keys should be requested from a QuantumModule (optional, uses default if not provided) + KeyFetchAmount: 10 # amount of keys to be requested from a QuantumModule (optional, uses default if not provided) + MaxKeyFillLevel: 100 # maximum number of keys to be stored in the storage related to one KMS peer (should be the same for the peer on the other side, uses default if not provided) # peer to goKMS03 - PeerId: "f80db2c0-2480-46b9-b7d1-b63f954e8227" PeerInterComAddr: 172.100.20.12:50910 diff --git a/goKMS/config/config.go b/goKMS/config/config.go index 44551cfdcebba2ddc5ddbc9cd010524915a4d155..6292e3475b01ad2673cb775dfcaf1cf2e0457b3e 100644 --- a/goKMS/config/config.go +++ b/goKMS/config/config.go @@ -34,12 +34,15 @@ type TLSConfig struct { } type QuantumModule struct { - QmType string `yaml:"Type"` - Address string `yaml:"Address"` - Hostname string `yaml:"Hostname"` - LocalSAEID string `yaml:"LocalSAEID"` - TargetSAEID string `yaml:"TargetSAEID"` - MasterMode bool `yaml:"MasterMode"` + QmType string `yaml:"Type"` + Address string `yaml:"Address"` + Hostname string `yaml:"Hostname"` + LocalSAEID string `yaml:"LocalSAEID"` + TargetSAEID string `yaml:"TargetSAEID"` + MasterMode bool `yaml:"MasterMode"` + KeyFetchInterval int `yaml:"KeyFetchInterval"` + KeyFetchAmount int `yaml:"KeyFetchAmount"` + MaxKeyFillLevel int `yaml:"MaxKeyFillLevel"` } type ETSI14Server struct { diff --git a/goKMS/gnmiHandlers/kms/keyStoreHandler.go b/goKMS/gnmiHandlers/kms/keyStoreHandler.go new file mode 100644 index 0000000000000000000000000000000000000000..e2785ecb9a9fd83bc1549405e35dd4619c237178 --- /dev/null +++ b/goKMS/gnmiHandlers/kms/keyStoreHandler.go @@ -0,0 +1,139 @@ +package kmsHandler + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gnmi-target/handler" + "code.fbi.h-da.de/danet/quant/goKMS/kms" + "code.fbi.h-da.de/danet/quant/goKMS/kms/event" + gnmitargetygot "code.fbi.h-da.de/danet/quant/goKMS/model" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" + "github.com/sirupsen/logrus" +) + +type KeyStoreHandler struct { + handler.DefaultPathHandler + kms *kms.KMS + maxKeyFillLevelsDefined map[string]uint64 + events <-chan event.Event +} + +type keyFillLevel struct { + storeID string + fillLevel uint64 +} + +func NewKeyStoreHandler(kms *kms.KMS, maxKeyFillLevelsDefined map[string]uint64) *KeyStoreHandler { + return &KeyStoreHandler{ + DefaultPathHandler: handler.DefaultPathHandler{ + Name: "key-store-handler", + Paths: map[string]struct{}{ + "key-stores": {}, + }, + }, + kms: kms, + maxKeyFillLevelsDefined: maxKeyFillLevelsDefined, + } +} + +func (yh *KeyStoreHandler) Init(config *handler.Config, publishToSubsFunc func([]*gnmi.Notification) error) error { + yh.Config = config + yh.PublishToSubs = publishToSubsFunc + + var err error + yh.events, err = yh.kms.EventBus().Subscribe(event.KEY_STORE) + if err != nil { + return err + } + + _, err = yh.updateOrCreateKeyStoreHandler() + if err != nil { + return err + } + + // Start the go routine that takes care of any update from the kms + go func() { + for { + select { + case <-yh.events: + logrus.Println("Update for KeyStores.") + _, err := yh.updateOrCreateKeyStoreHandler() + if err != nil { + logrus.Errorf("Error within key stores subscription goroutine; %v", err) + } + + // gnmi subscribe things here? + } + } + }() + + return nil +} + +func (yh *KeyStoreHandler) Update(c ygot.ValidatedGoStruct, jobs []*gnmi.Update) error { + return nil +} + +func (yh *KeyStoreHandler) updateOrCreateKeyStoreHandler() ([]*gnmi.Notification, error) { + yh.Config.Lock() + defer yh.Config.Unlock() + + copyCurrentConfig, err := ygot.DeepCopy(yh.Config.Data) + if err != nil { + return nil, err + } + + newConfig, ok := copyCurrentConfig.(*gnmitargetygot.Gnmitarget) + if !ok { + return nil, fmt.Errorf("Wrong type, exptected: %T, got: %T", (*gnmitargetygot.Temp_KeyStores)(nil), copyCurrentConfig) + } + + confKeyStores := newConfig.GetOrCreateKeyStores() + + keyFillLevels := getKeyFillLevels(yh.kms) + + // TODO: Maybe add more config values here! + for _, keyFillLevel := range keyFillLevels { + confKeyStoreContainer := confKeyStores.GetOrCreateKeyStore(keyFillLevel.storeID) + + confKeyStoreContainer.KmsPeerId = ygot.String(keyFillLevel.storeID) + confKeyStore := confKeyStoreContainer.GetOrCreateKeyStore() + + confKeyStore.KeyFillLevel = ygot.Uint64(keyFillLevel.fillLevel) + maxKeyFillLevel, ok := yh.maxKeyFillLevelsDefined[keyFillLevel.storeID] + if !ok { + return nil, fmt.Errorf("no max key fill level available for store with ID: %s", keyFillLevel.storeID) // TODO(faseid): check if really want to return here?! + } + + confKeyStore.MaxKeyFillLevel = ygot.Uint64(maxKeyFillLevel) + } + + // validate struct + if err := newConfig.Validate(); err != nil { + return nil, err + } + + notifications, err := ygot.DiffWithAtomic(yh.Config.Data, newConfig) + if err != nil { + return nil, err + } + + yh.Config.Data = newConfig + + return notifications, nil +} + +func getKeyFillLevels(kms *kms.KMS) []keyFillLevel { + kmsPeers := kms.KmsPeers + keyFillLevels := []keyFillLevel{} + + for _, peer := range kmsPeers { + keyFillLevels = append(keyFillLevels, keyFillLevel{ + storeID: peer.GetKmsPeerId().String(), + fillLevel: uint64(peer.GetKeyStore().Length()), + }) + } + + return keyFillLevels +} diff --git a/goKMS/kms/event/event.go b/goKMS/kms/event/event.go index b99faee99ccabaf0ca246dff53294935ce7961ac..a0f3e83036776f54fca01d596cd41656ba97f5dd 100644 --- a/goKMS/kms/event/event.go +++ b/goKMS/kms/event/event.go @@ -9,6 +9,7 @@ const ( ROUTE QUANTUM_MODULE CREATE_ROUTE + KEY_STORE ) // Event ... @@ -86,3 +87,23 @@ func (e *RouteEvent) Topic() Topic { func (e *RouteEvent) Time() time.Time { return e.Timestamp } + +type KeyStoresEvent struct { + EventTopic Topic + Timestamp time.Time +} + +func NewKeyStoresEvent() *KeyStoresEvent { + return &KeyStoresEvent{ + EventTopic: KEY_STORE, + Timestamp: time.Now(), + } +} + +func (e *KeyStoresEvent) Topic() Topic { + return e.EventTopic +} + +func (e *KeyStoresEvent) Time() time.Time { + return e.Timestamp +} diff --git a/goKMS/kms/kms.go b/goKMS/kms/kms.go index 12ac106eabc25504d3b43fe36dd96038f052109c..848590faad9b02144cca10b90500e030ad67743e 100644 --- a/goKMS/kms/kms.go +++ b/goKMS/kms/kms.go @@ -190,7 +190,10 @@ func (kms *KMS) initializePeers(config *config.Config) error { case "emulated": qm = peers.NewDanetQuantumModule(pqm.Address, config.Id) case "etsi": - qm, err = peers.NewETSI014HTTPQuantumModule(pqm.Address, config.Id, pqm.LocalSAEID, pqm.TargetSAEID, config.QuantumModuleTLS, pqm.MasterMode) + qm, err = peers.NewETSI014HTTPQuantumModule(pqm.Address, config.Id, pqm.LocalSAEID, pqm.TargetSAEID, + config.QuantumModuleTLS, pqm.MasterMode, + peer.QuantumModule.KeyFetchInterval, int64(peer.QuantumModule.KeyFetchAmount), uint64(peer.QuantumModule.MaxKeyFillLevel), + kms.eventBus) if err != nil { log.Fatalf("Failed to create ETSI QKD module: %s", err) return nil diff --git a/goKMS/kms/peers/etsi14Quantummodule.go b/goKMS/kms/peers/etsi14Quantummodule.go index 7e0187202e7089e184022195f3436e035ff90a5b..97d164c286ec1ff8aab9f36061eebe4d7932ae9d 100644 --- a/goKMS/kms/peers/etsi14Quantummodule.go +++ b/goKMS/kms/peers/etsi14Quantummodule.go @@ -18,19 +18,32 @@ import ( log "github.com/sirupsen/logrus" ) +const ( + maxFailedKeyRequestAttempts = 10 + defaultKeyFetchInterval = 10 + defaultKeyFetchAmount = int64(1) + defualtMaxKeyFillLevel = uint64(100) + + backgroundKeyStoreUpdateInterval = 1 +) + type ETSI014HTTPQuantumModule struct { - id uuid.UUID - kmsId string - addr string - keyStore *store.KmsKeyStore - kmsClient *GRPCClient - client *etsi14ClientImpl.ClientImpl - localSAEID string - targetSAEID string - master bool + id uuid.UUID + kmsId string + addr string + keyStore *store.KmsKeyStore + kmsClient *GRPCClient + client *etsi14ClientImpl.ClientImpl + localSAEID string + targetSAEID string + master bool + keyFetchInterval int + keyFetchAmount int64 + maxKeyFillLevel uint64 + kmsEventBus *event.EventBus } -func NewETSI014HTTPQuantumModule(addr, kmsId, localSAEID, targetSAEID string, tlsConfig config.TLSConfig, master bool) (*ETSI014HTTPQuantumModule, error) { +func NewETSI014HTTPQuantumModule(addr, kmsId, localSAEID, targetSAEID string, tlsConfig config.TLSConfig, master bool, keyFetchInterval int, keyFetchAmount int64, maxKeyFillLevel uint64, kmsEventBus *event.EventBus) (*ETSI014HTTPQuantumModule, error) { parsedUrl, err := url.Parse(addr) if err != nil { return nil, err @@ -65,16 +78,33 @@ func NewETSI014HTTPQuantumModule(addr, kmsId, localSAEID, targetSAEID string, tl return nil, err } + // set defaults for key fetching if not defined in config + if keyFetchInterval == 0 { + keyFetchInterval = defaultKeyFetchInterval + } + + if keyFetchAmount == 0 { + keyFetchAmount = defaultKeyFetchAmount + } + + if maxKeyFillLevel == 0 { + maxKeyFillLevel = defualtMaxKeyFillLevel + } + return &ETSI014HTTPQuantumModule{ - id: uuid.New(), - kmsId: kmsId, - addr: addr, - keyStore: store.NewKmsKeyStore(256), - kmsClient: nil, - client: client, - localSAEID: localSAEID, - targetSAEID: targetSAEID, - master: master, + id: uuid.New(), + kmsId: kmsId, + addr: addr, + keyStore: store.NewKmsKeyStore(256), + kmsClient: nil, + client: client, + localSAEID: localSAEID, + targetSAEID: targetSAEID, + master: master, + keyFetchInterval: keyFetchInterval, + keyFetchAmount: keyFetchAmount, + maxKeyFillLevel: maxKeyFillLevel, + kmsEventBus: kmsEventBus, }, nil } @@ -87,38 +117,54 @@ func (qm *ETSI014HTTPQuantumModule) Client() *etsi14ClientImpl.ClientImpl { } func (qm *ETSI014HTTPQuantumModule) Initialize() error { - // start polling + // sends events on the event bus every x seconds to keep key store config updated + go qm.runBackgroundKeyStoreUpdates() + + // start polling keys if qm.master { go func() { - ticker := time.NewTicker(2 * time.Second) + ticker := time.NewTicker(time.Duration(qm.keyFetchInterval) * time.Second) defer ticker.Stop() + failedAttemps := 0 + // TODO: add context/channel to stop for range ticker.C { - container, err := qm.GetKeys(1, 256, nil, nil, nil) - if err != nil { - log.Error(err) + if failedAttemps == maxFailedKeyRequestAttempts { + log.Errorf("stopped trying to fetch keys from qkd module after %d tries", failedAttemps) break } - keyIds := make([]string, len(container.GetKeys())) - for i, keyItem := range container.GetKeys() { - keyIds[i] = keyItem.GetKeyID() - } - - _, err = qm.kmsClient.KeyIdNotification(context.Background(), - &pbIC.KeyIdNotificationRequest{ - Timestamp: time.Now().Unix(), - KmsId: qm.kmsId, - KeyIds: keyIds, - }) - if err != nil { - log.Error(err) - break - } - - if err := store.AddETSIKeysToKeystore(qm.keyStore, container.GetKeys()); err != nil { - log.Error(err) + if qm.keyStore.Length() < int(qm.maxKeyFillLevel) { + container, err := qm.GetKeys(qm.keyFetchAmount, 256, nil, nil, nil) + if err != nil { + log.Error(err) + failedAttemps++ + continue + } + + keyIds := make([]string, len(container.GetKeys())) + for i, keyItem := range container.GetKeys() { + keyIds[i] = keyItem.GetKeyID() + } + + _, err = qm.kmsClient.KeyIdNotification(context.Background(), + &pbIC.KeyIdNotificationRequest{ + Timestamp: time.Now().Unix(), + KmsId: qm.kmsId, + KeyIds: keyIds, + }) + if err != nil { + log.Error(err) + failedAttemps++ + continue + } + + if err := store.AddETSIKeysToKeystore(qm.keyStore, container.GetKeys()); err != nil { + log.Error(err) + } + + failedAttemps = 0 } } }() @@ -179,3 +225,15 @@ func (qm *ETSI014HTTPQuantumModule) GetKeyWithIds(keyIds []etsi14ClientGenerated return container, nil } + +func (qm *ETSI014HTTPQuantumModule) runBackgroundKeyStoreUpdates() { + ticker := time.NewTicker(backgroundKeyStoreUpdateInterval * time.Second) + defer ticker.Stop() + + for range ticker.C { + err := qm.kmsEventBus.Publish(event.NewKeyStoresEvent()) + if err != nil { + log.Error(err) + } + } +} diff --git a/goKMS/kms/peers/kmsPeer.go b/goKMS/kms/peers/kmsPeer.go index a6828434065e0b49f2b3ec1bca9be3d5b88c661f..3581b8ec19f6c0e25b9c26eaaaaaaad4f3d07474 100644 --- a/goKMS/kms/peers/kmsPeer.go +++ b/goKMS/kms/peers/kmsPeer.go @@ -10,6 +10,7 @@ import ( pbIC "code.fbi.h-da.de/danet/quant/goKMS/api/gen/proto/go/kmsintercom" "code.fbi.h-da.de/danet/quant/goKMS/kms/crypto" "code.fbi.h-da.de/danet/quant/goKMS/kms/event" + "code.fbi.h-da.de/danet/quant/goKMS/kms/store" "code.fbi.h-da.de/danet/quant/goKMS/kms/util" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -214,3 +215,7 @@ func (kp *KmsPeer) SetStatus(updateStatus KmsPeerStatus) { func (kp *KmsPeer) GetKmsPeerId() uuid.UUID { return kp.peerKmsId } + +func (kp *KmsPeer) GetKeyStore() *store.KmsKeyStore { + return kp.servingQuantumModul.KeyStore() +} diff --git a/goKMS/main.go b/goKMS/main.go index 51d6ade05bd16142771c94dd80ea6d349f2afabf..aca7e904cd79616773e1500baa83f02525f46c8f 100644 --- a/goKMS/main.go +++ b/goKMS/main.go @@ -124,6 +124,8 @@ func main() { log.Fatal(err) } + maxKeyFillLevels := getMaxKeyFillLevelsFromConfig(kmsConfig.Peers) + // The registered path handlers sorted by priority. If specific // handlers should be able to process their workload before others, // then they should be placed in the front of the slice. @@ -137,6 +139,7 @@ func main() { kmsHandler.NewPeerHandler(kms), kmsHandler.NewKeyRoutingSessionHandler(kms), kmsHandler.NewAssignForwardingHandler(kms), + kmsHandler.NewKeyStoreHandler(kms, maxKeyFillLevels), } // The gnmiTarget implementation uses a flag to pass NO tls, so we have to invert our flag for it to work. @@ -233,3 +236,13 @@ func setupQkdnManagerServer(kms *kms.KMS, config config.QkdnManagerServer) { cancel() } + +func getMaxKeyFillLevelsFromConfig(peers []config.Peer) map[string]uint64 { + maxKeyFillLevels := make(map[string]uint64, 0) + + for _, peer := range peers { + maxKeyFillLevels[peer.PeerId] = uint64(peer.QuantumModule.MaxKeyFillLevel) + } + + return maxKeyFillLevels +}