Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
kms.go 6.38 KiB
// This package kms implements a simplistic key management system (kms) for
// Quantum Key Distribution Networks (QKDN) which is a simple emulated KMS. x
// It relies on the emulated quantum link out of the quantumlayer package

package kms

import (
	"fmt"
	"io"
	"net"
	"sync"

	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"

	pbETSI "code.fbi.h-da.de/danet/quant/ekms/internal/api/gen/proto/go/kmsetsi"
	pbIC "code.fbi.h-da.de/danet/quant/ekms/internal/api/gen/proto/go/kmsintercom"
	"code.fbi.h-da.de/danet/quant/ekms/internal/kms/event"
	pbQS "code.fbi.h-da.de/danet/quipsec/gen/go/quipsec"
	"github.com/google/uuid"
)

type Route struct {
	PathId   uuid.UUID
	Previous *kmsPeer
	Next     *kmsPeer
}

type BitKeyLength string

const (
	BitKeyLen128 BitKeyLength = "128"
	BitKeyLen256 BitKeyLength = "256"
	BitKeyLen512 BitKeyLength = "512"
)

// The general emulated KMS.
type EKMS struct {
	kmsName      string
	kmsUUID      uuid.UUID
	interComAddr string
	// TODO create a mapping between ids and address
	quantumModules      map[string]QuantumModule
	quantumModulesMutex sync.RWMutex
	kmsPeersMutex       sync.Mutex
	// TODO(maba): find a better name for this
	// TODO: add mutex
	keysForPathId     map[uuid.UUID]string
	routingTable      map[uuid.UUID]*Route
	routingTableMutex sync.RWMutex
	KmsPeers          map[string]*kmsPeer
	pbETSI.UnimplementedKmsETSIServer
	pbIC.UnimplementedKmsTalkerServer
	supportedKeyLengths map[BitKeyLength]bool
	eventBus            *event.EventBus
}

// Will keep information about the quantum elements that this EKMS is talking to
// This actually constitutes a quantum element with only a single link

/*
type QuantumElementInterface interface {
	GetQlID() qlElementId
}*/

func NewEKMS(kmsName string, kmsUUID uuid.UUID, logOutput io.Writer, logLevel log.Level, logInJson bool, interComAddr string) (newEKMS *EKMS) {
	/*
	 * Setup logging
	 */
	//What level
	log.SetLevel(logLevel)
	// Where to send log out put
	log.SetOutput(logOutput)
	// and plain-text (standard) or json
	if !logInJson {
		log.SetFormatter(&log.TextFormatter{})
	} else {
		log.SetFormatter(&log.JSONFormatter{})
	}
	// print code function if level is set to Trace
	if logLevel == log.TraceLevel {
		log.SetReportCaller(true)
	} else {
		log.SetReportCaller(false)
	}

	createdEKMS := &EKMS{
		kmsName:             kmsName,
		kmsUUID:             kmsUUID,
		interComAddr:        interComAddr,
		quantumModules:      make(map[string]QuantumModule),
		routingTable:        make(map[uuid.UUID]*Route),
		KmsPeers:            make(map[string]*kmsPeer),
		supportedKeyLengths: make(map[BitKeyLength]bool),
		eventBus:            event.NewEventBus(),
	}

	createdEKMS.supportedKeyLengths[BitKeyLen256] = true

	// start the inter communication gRPC server
	go createdEKMS.startGRPC(interComAddr)

	return createdEKMS
}

func (kms *EKMS) startGRPC(interComAddr string) {
	lis, err := net.Listen("tcp", interComAddr)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	healthCheck := health.NewServer()

	healthpb.RegisterHealthServer(s, healthCheck)
	pbIC.RegisterKmsTalkerServer(s, &kmsTalkerServer{
		keyNegotiationMap: make(map[uuid.UUID]*kmsKSElement),
		eKMS:              kms,
	})
	pbQS.RegisterKmsQkdmCommunicationServiceServer(s, &quipSecServer{
		eKMS: kms,
	})

	go func() {
		// set status to serving
		healthCheck.SetServingStatus(pbIC.KmsTalker_ServiceDesc.ServiceName, healthpb.HealthCheckResponse_SERVING)
		// TODO: add logic for adjusting health status based on operating status of
		// the services
		//    for{}
	}()

	log.Infof("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

func (kms *EKMS) AddQuantumElement(qm QuantumModule) error {
	kms.quantumModulesMutex.Lock()
	log.Info(qm.Address())
	kms.quantumModules[qm.Address()] = qm
	kms.quantumModulesMutex.Unlock()
	return nil
}

func (kms *EKMS) AddPeer(peerKmsId string, kmsPeerSocket string, servingQLE QuantumModule) (*kmsPeer, error) {
	// check if peer exists
	if _, there := kms.KmsPeers[kmsPeerSocket]; there {
		log.Errorf("Trying to add existing peer %s", kmsPeerSocket)
		return nil, fmt.Errorf("Trying to add existing peer %s", kmsPeerSocket)
	}
	peer, err := NewKmsPeer(peerKmsId, servingQLE, kmsPeerSocket, kms.interComAddr, kms.eventBus)
	if err != nil {
		return nil, err
	}
	peer.tcpSocketStr = kmsPeerSocket

	kms.kmsPeersMutex.Lock()
	kms.KmsPeers[kmsPeerSocket] = peer
	kms.kmsPeersMutex.Unlock()

	// go peer.PeerHandler(kms.kmsName)
	return peer, nil
}

func (kms *EKMS) AssignForwardingRoute(pId, pHop, nHop string) error {
	pathId, err := uuid.Parse(pId)
	if err != nil {
		return fmt.Errorf("The given path id %s is no uuid; err = %w", pathId, err)
	}

	var previousHop *kmsPeer
	var nextHop *kmsPeer
	var ok bool
	if pHop != "" {
		previousHop, ok = kms.KmsPeers[pHop]
		if !ok {
			return fmt.Errorf("No peer found for %s", pHop)
		}
	}
	if nHop != "" {
		nextHop, ok = kms.KmsPeers[nHop]
		if !ok {
			return fmt.Errorf("No peer found for %s", nHop)
		}
	}

	kms.routingTableMutex.Lock()
	// set the route within routing table
	kms.routingTable[pathId] = &Route{
		PathId:   pathId,
		Previous: previousHop,
		Next:     nextHop,
	}
	kms.routingTableMutex.Unlock()

	err = kms.eventBus.Publish(event.NewRouteEvent())
	if err != nil {
		log.Error(err)
	}

	return nil
}

func (kms *EKMS) EventBus() *event.EventBus {
	return kms.eventBus
}

// TODO/XXX error handling.
func (kms *EKMS) RemovePeer(kmsPeerSocket string) {
	if _, there := kms.KmsPeers[kmsPeerSocket]; there {
		// peer.quit <- true
		delete(kms.KmsPeers, kmsPeerSocket)
		return
	}
	log.Errorf("%s: Can not find a peer with socket: %s", kms.kmsName, kmsPeerSocket)
}

func (kms *EKMS) FindPeerUuid(lookup uuid.UUID) (peer *kmsPeer) {
	if kms.KmsPeers != nil {
		for _, peer = range kms.KmsPeers {
			if peer.id == lookup {
				return peer
			}
		}
	}

	return nil
}

func (kms *EKMS) RoutingTableDeepCopy() map[uuid.UUID]*Route {
	copy := make(map[uuid.UUID]*Route, len(kms.KmsPeers))

	kms.routingTableMutex.Lock()
	for k, v := range kms.routingTable {
		copy[k] = v
	}
	kms.routingTableMutex.Unlock()

	return copy
}

func (kms *EKMS) PeersDeepCopy() map[string]*kmsPeer {
	copy := make(map[string]*kmsPeer, len(kms.KmsPeers))

	kms.kmsPeersMutex.Lock()
	for k, v := range kms.KmsPeers {
		copy[k] = v
	}
	kms.kmsPeersMutex.Unlock()

	return copy
}