Skip to content
Snippets Groups Projects
kms.go 22 KiB
Newer Older
  • Learn to ignore specific revisions
  • Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    // 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 (
    
    	"context"
    	"encoding/base64"
    
    	"time"
    
    	log "github.com/sirupsen/logrus"
    
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/health"
    	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    
    	"google.golang.org/grpc/metadata"
    
    	pbIC "code.fbi.h-da.de/danet/quant/goKMS/api/gen/proto/go/kmsintercom"
    	"code.fbi.h-da.de/danet/quant/goKMS/config"
    
    	akmsInterfaceClient "code.fbi.h-da.de/danet/quant/goKMS/kms/akmsInterface/client"
    	akmsInterfaceServer "code.fbi.h-da.de/danet/quant/goKMS/kms/akmsInterface/server"
    
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/crypto"
    
    	etsi14Server "code.fbi.h-da.de/danet/quant/goKMS/kms/etsi/etsi14/server"
    
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/event"
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/peers"
    
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/receiver"
    
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/store"
    
    	kmstls "code.fbi.h-da.de/danet/quant/goKMS/kms/tls"
    
    	"code.fbi.h-da.de/danet/quant/goKMS/kms/util"
    
    	pbQS "code.fbi.h-da.de/danet/quipsec/gen/go/quipsec"
    
    type Route struct {
    
    	Previous  *peers.KmsPeer
    	Next      *peers.KmsPeer
    
    	RemoteKMS *util.RemoteKMS
    
    type BitKeyLength string
    
    const (
    	BitKeyLen128 BitKeyLength = "128"
    	BitKeyLen256 BitKeyLength = "256"
    	BitKeyLen512 BitKeyLength = "512"
    )
    
    type PlatformKey struct {
    	Id        uuid.UUID
    	Value     []byte
    	ProcessId string
    }
    
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    // The general emulated KMS.
    
    type KMS struct {
    
    	kmsName        string
    	kmsUUID        uuid.UUID
    	interComAddr   string
    	quantumAddress string
    
    	gRPCTimeout    time.Duration
    
    	// TODO create a mapping between ids and address
    
    	remoteKMSMapping      map[string]*util.RemoteKMS
    
    	quantumModules        map[string]peers.QuantumModule
    
    	kmsPeersMutex         sync.RWMutex
    
    	// NOTE: There is probably a better way to handle this
    
    	PKStore      map[string]map[uuid.UUID]*PlatformKey
    
    	PKStoreMutex sync.Mutex
    
    	// TODO(maba): find a better name for this
    
    	routingTable      map[uuid.UUID]*Route
    	routingTableMutex sync.RWMutex
    
    	KmsPeers          map[string]*peers.KmsPeer
    
    	pbIC.UnimplementedKmsTalkerServer
    
    	supportedKeyLengths map[BitKeyLength]bool
    
    	receiver            *receiver.Receiver
    	// Akms things
    
    	ckmsAkmsClient *akmsInterfaceClient.CkmsAkmsClient
    	ckmsAkmsServer *akmsInterfaceServer.AKMSReceiverServer
    
    	// ETSI14 Server things
    
    	etsi14Server    *etsi14Server.ETSI14RESTService
    	keyStoreChannel chan []crypto.KSAKey
    
    // 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 NewKMS(kmsUUID uuid.UUID, logOutput io.Writer, logLevel log.Level, logInJson bool, config *config.Config, receiver *receiver.Receiver) (newKMS *KMS) {
    
    	/*
    	 * 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)
    	}
    
    	var ckmsAkmsClient *akmsInterfaceClient.CkmsAkmsClient
    	var err error
    
    	if config.AKMS.RemoteAddress != "" {
    		ckmsAkmsClient, err = akmsInterfaceClient.NewCkmsAkmsClient(config.AKMS.RemoteAddress, config.AKMS.ClientTLS)
    
    		if err != nil {
    			log.Fatalf("Failed to setup CkmsAkmsClient: %s", err)
    		}
    
    	gRPCTimeoutInSecondsDuration := time.Duration(config.GRPCTimeoutInSeconds) * time.Second
    
    
    	createdKMS := &KMS{
    
    		kmsName:             config.Name,
    
    		interComAddr:        config.InterComAddr,
    		quantumAddress:      config.QuantumAddr,
    
    		gRPCTimeout:         gRPCTimeoutInSecondsDuration,
    
    		remoteKMSMapping:    make(map[string]*util.RemoteKMS),
    
    		quantumModules:      make(map[string]peers.QuantumModule),
    
    		routingTable:        make(map[uuid.UUID]*Route),
    
    		PKStore:             make(map[string]map[uuid.UUID]*PlatformKey),
    
    		KmsPeers:            make(map[string]*peers.KmsPeer),
    
    		supportedKeyLengths: make(map[BitKeyLength]bool),
    		eventBus:            event.NewEventBus(),
    
    		ckmsAkmsClient:      ckmsAkmsClient,
    
    		receiver:            receiver,
    
    	createdKMS.supportedKeyLengths[BitKeyLen256] = true
    
    	// start the inter communication gRPC server
    
    
    	// initialize from config
    
    	err = createdKMS.initializePeers(config)
    
    	if err != nil {
    		log.Fatalf("Failed to initialize peers: %s", err)
    	}
    
    	// Start the akmsCkmsReceiverServer
    
    	if config.AKMS.ServerPort != "" {
    		createdKMS.ckmsAkmsServer, err = akmsInterfaceServer.NewAKMSReceiver(config.AKMS.ServerPort, createdKMS.eventBus, receiver, createdKMS.GenerateAndSendKSAKey, config.AKMS.ServerTLS)
    
    		if err != nil {
    			log.Fatalf("Failed to initialize CkmsAkmsServer: %s", err)
    		}
    
    		log.Infof("Starting AKMS receiver server on port: %s", config.AKMS.ServerPort)
    
    		go createdKMS.ckmsAkmsServer.Serve()
    	}
    
    	// Setup ETSI14 Server if info is in config
    	if config.ETSI14Server != nil {
    		remoteCKMSID, err := uuid.Parse(config.ETSI14Server.RemoteCKMSID)
    		if err != nil {
    			log.Error(err)
    		}
    
    		createdKMS.etsi14Server = etsi14Server.NewETSI14RESTService(config.ETSI14Server.Address, remoteCKMSID, createdKMS.exchangeKeyAfterETSI14GetKeyRequest)
    		go createdKMS.etsi14Server.Run()
    		createdKMS.keyStoreChannel = createdKMS.etsi14Server.KeyStoreChannel
    	} else {
    		log.Info("No ETSI14 server running.")
    
    	return createdKMS
    
    func (kms *KMS) initializePeers(config *config.Config) error {
    
    	var qm peers.QuantumModule
    	var err error
    
    	for _, peer := range config.Peers {
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		err = resolveHostnameToIPForQuantumModule(&peer)
    		if err != nil {
    			log.Error(err)
    			continue
    		}
    
    
    		pqm := peer.QuantumModule
    		switch qmt := peer.QuantumModule.QmType; qmt {
    		case "emulated":
    
    			qm = peers.NewDanetQuantumModule(pqm.Address, config.Id)
    
    		case "etsi":
    
    			qm, err = peers.NewETSI014HTTPQuantumModule(pqm.Address, config.Id, pqm.LocalSAEID, pqm.TargetSAEID,
    
    				peer.QuantumModule.TLS, pqm.MasterMode,
    
    				peer.QuantumModule.KeyFetchInterval, int64(peer.QuantumModule.KeyFetchAmount), uint64(peer.QuantumModule.MaxKeyFillLevel))
    
    			if err != nil {
    				log.Fatalf("Failed to create ETSI QKD module: %s", err)
    				return nil
    			}
    		default:
    			log.Fatalf("Unknown type: %s for quantum module", qmt)
    			return nil
    		}
    
    
    		err := kms.AddQuantumElement(qm)
    
    		if err != nil {
    			log.Fatalf("Failed to add quantum element: %s", err)
    			return nil
    		}
    
    
    		gRPCTransportCreds, err := kmstls.GenerateGRPCClientTransportCredsBasedOnTLSFlag(kms.tlsConfig)
    		if err != nil {
    			return fmt.Errorf("unable to generate gRPC transport creds: %w", err)
    
    		newPeerConn, err := grpc.NewClient(peer.PeerInterComAddr, grpc.WithTransportCredentials(gRPCTransportCreds))
    
    		if err != nil {
    			return nil
    		}
    
    		client := &peers.GRPCClient{}
    		switch pt := peer.Type; pt {
    		case "danet":
    			client.KmsTalkerClient = pbIC.NewKmsTalkerClient(newPeerConn)
    		}
    
    
    		_, err = kms.AddPeer(peer.PeerId, peer.PeerInterComAddr, qm, client)
    
    		if err != nil {
    			log.Fatalf("Failed to create a peer: %s", err)
    			return nil
    		}
    	}
    	return nil
    }
    
    
    func (kms *KMS) startGRPC() {
    	interKMSLis, err := net.Listen("tcp", kms.interComAddr)
    
    	if err != nil {
    		log.Fatalf("failed to listen: %v", err)
    	}
    
    	gRPCTransportCreds, err := kmstls.GenerateGRPCServerTransportCredsBasedOnTLSFlag(kms.tlsConfig)
    	if err != nil {
    		log.Fatalf("unable to generate gRPC server: %v", err)
    
    	}
    
    	interKMSServer := grpc.NewServer(grpc.Creds(gRPCTransportCreds))
    
    	healthCheck := health.NewServer()
    
    
    	healthpb.RegisterHealthServer(interKMSServer, healthCheck)
    	pbIC.RegisterKmsTalkerServer(interKMSServer, &kmsTalkerServer{
    
    		keyNegotiationMap: make(map[uuid.UUID]*store.KmsKSElement),
    
    		kms:               kms,
    
    	if kms.quantumAddress != "" {
    		quantumLis, err := net.Listen("tcp", kms.quantumAddress)
    
    		if err != nil {
    			log.Fatalf("failed to listen: %v", err)
    		}
    		quantumServ := grpc.NewServer()
    		pbQS.RegisterKmsQkdmCommunicationServiceServer(quantumServ, &quipSecServer{
    			KMS: kms,
    		})
    
    		log.Infof("quantum server listening at %v", quantumLis.Addr())
    		go quantumServ.Serve(quantumLis) //nolint:errcheck
    
    
    	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("inter KMS server listening at %v", interKMSLis.Addr())
    	if err := interKMSServer.Serve(interKMSLis); err != nil {
    
    		log.Fatalf("failed to serve: %v", err)
    	}
    }
    
    
    func (kms *KMS) AddQuantumElement(qm peers.QuantumModule) error {
    
    	kms.quantumModulesMutex.Lock()
    
    	defer kms.quantumModulesMutex.Unlock()
    
    	log.Infof("quantum module address: %s ", qm.Address())
    
    	kms.quantumModules[qm.Address()] = qm
    
    func (kms *KMS) AddPeer(peerKmsId string, kmsPeerSocket string, servingQLE peers.QuantumModule, client *peers.GRPCClient) (*peers.KmsPeer, error) {
    
    	// check if peer exists
    
    	_, err := kms.FindPeerById(peerKmsId)
    	if err == nil {
    
    		log.Errorf("Trying to add existing peer %s, with KMS ID %s", kmsPeerSocket, peerKmsId)
    
    		return nil, fmt.Errorf("trying to add existing peer %s, with KMS ID %s", kmsPeerSocket, peerKmsId)
    
    	peer, err := peers.NewKmsPeer(peerKmsId, servingQLE, kmsPeerSocket, client, kms.eventBus, kms.gRPCTimeout)
    
    	if err != nil {
    		return nil, err
    
    	peer.TcpSocketStr = kmsPeerSocket
    
    	kms.KmsPeers[peerKmsId] = peer
    
    	go func() {
    		restartWaitingTime := time.Duration(3) * time.Second
    		ticker := time.NewTicker(restartWaitingTime)
    		defer ticker.Stop()
    
    	InitialResetLoop:
    		for !peer.QuantumModule().IsActive() {
    			select {
    			case <-ticker.C:
    				if client := peer.Client().KmsTalkerClient; client != nil {
    					log.Debugf("Sending initial peer setup request to peer: %s with the request to reset the corresponding key store", peerKmsId)
    					ctx, cancel := context.WithTimeout(context.Background(), kms.gRPCTimeout)
    					defer cancel()
    					_, err := client.InterComCapabilities(ctx, &pbIC.InterComCapabilitiesRequest{
    						Timestamp:     time.Now().Unix(),
    						KmsId:         kms.kmsUUID.String(),
    						ResetKeyStore: true,
    					})
    					if err == nil {
    						if err := peer.QuantumModule().Initialize(); err != nil {
    							log.Fatalf("Failed to initialized quantum module: %s for peer: %s", peer.QuantumModule().ID(), peer.GetKmsPeerId())
    						}
    						log.Debugf("Successfully initialized quantum module: %s for peer %s", peer.QuantumModule().ID(), peerKmsId)
    						break InitialResetLoop
    					}
    				}
    			}
    		}
    		log.Debugf("Successfully initialized peer: %s", peerKmsId)
    	}()
    
    
    	return peer, nil
    
    func (kms *KMS) AssignForwardingRoute(pId, pHop, nHop string, remoteKMS *util.RemoteKMS) 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 *peers.KmsPeer
    	var nextHop *peers.KmsPeer
    
    	if pHop != "" {
    
    		previousHop, err = kms.FindPeerById(pHop)
    		if err != nil {
    
    			return fmt.Errorf("no peer found for %s", pHop)
    
    		nextHop, err = kms.FindPeerById(nHop)
    		if err != nil {
    			return fmt.Errorf("no peer found for %s", pHop)
    
    	tmpRoute := &Route{
    
    		PathId:    pathId,
    		Previous:  previousHop,
    		Next:      nextHop,
    		RemoteKMS: remoteKMS,
    
    	kms.routingTableMutex.Lock()
    
    	// set the route within routing table
    
    	kms.routingTable[pathId] = tmpRoute
    
    	kms.routingTableMutex.Unlock()
    
    
    	if tmpRoute.RemoteKMS != nil {
    		kms.remoteKMSMappingMutex.Lock()
    		if _, ok := kms.remoteKMSMapping[tmpRoute.RemoteKMS.Id]; !ok {
    			kms.remoteKMSMapping[tmpRoute.RemoteKMS.Id] = tmpRoute.RemoteKMS
    		}
    		kms.remoteKMSMappingMutex.Unlock()
    	}
    
    
    	if tmpRoute.Previous == nil && tmpRoute.Next != nil && tmpRoute.RemoteKMS != nil {
    
    		// generate pk key
    		pk, err := crypto.Random256BitKey()
    		if err != nil {
    			return err
    		}
    
    
    		// generate process id
    		processId := uuid.New()
    
    
    		// update PKStore
    
    		kms.AddSpecificPlatformKey(tmpRoute.RemoteKMS.Id, pathId, processId, pk)
    
    		err = tmpRoute.Next.SendInitialPayloadBasedOnGRPCClient(pk, tmpRoute.PathId, processId, kms.kmsUUID.String(), remoteKMS)
    
    		if err != nil {
    			log.Error(err)
    			return err
    		}
    	}
    
    func (kms *KMS) AddSpecificPlatformKey(remoteKMSId string, pathId uuid.UUID, processId uuid.UUID, key *crypto.Key) {
    	kms.PKStoreMutex.Lock()
    	defer kms.PKStoreMutex.Unlock()
    
    	keys, ok := kms.PKStore[remoteKMSId]
    	if !ok {
    		kms.PKStore[remoteKMSId] = map[uuid.UUID]*PlatformKey{
    			pathId: {
    				Id:        key.ID,
    				Value:     key.Key,
    				ProcessId: processId.String(),
    			},
    		}
    	} else {
    		keys[pathId] = &PlatformKey{
    			Id:        key.ID,
    			Value:     key.Key,
    			ProcessId: processId.String(),
    		}
    	}
    
    	log.Debug("Current store of platform keys: ", kms.PKStore)
    }
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    func (kms *KMS) GetSpecificPlatformKey(remoteKMSId string, keyId uuid.UUID) (*PlatformKey, error) {
    
    	kms.PKStoreMutex.Lock()
    	defer kms.PKStoreMutex.Unlock()
    
    	keyIDs, ok := kms.PKStore[remoteKMSId]
    	if !ok {
    
    		return nil, fmt.Errorf("no entry for given KMS id: %s", remoteKMSId)
    
    		return nil, fmt.Errorf("no key ready to use for given KMS id: %s", remoteKMSId)
    
    func (kms *KMS) GetRemoteKMS(remoteKMSId string) (*util.RemoteKMS, error) {
    
    	kms.remoteKMSMappingMutex.RLock()
    	defer kms.remoteKMSMappingMutex.RUnlock()
    
    	remoteKMS, ok := kms.remoteKMSMapping[remoteKMSId]
    	if !ok {
    
    		return nil, fmt.Errorf("address for remoteKMS with id %s not found", remoteKMSId)
    
    	}
    	return remoteKMS, nil
    }
    
    // NOTE: address/remoteid still have to decide.
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    func (kms *KMS) GenerateAndSendKSAKey(remoteKMSId string, pathId uuid.UUID, requestID string, number int) error {
    
    	if number < 1 {
    		log.Errorf("number must be positive and at least 1, provided: %d\n", number)
    		return fmt.Errorf("number must be positive and at least 1, provided: %d", number)
    	}
    
    
    	log.Infof("KMS store: %v", kms.remoteKMSMapping)
    
    	remoteKMS, err := kms.GetRemoteKMS(remoteKMSId)
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	platformKey, err := kms.GetSpecificPlatformKey(remoteKMSId, pathId)
    
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	ksaKeys := make([]*pbIC.Key, number)
    
    	akmsKSAKeys := make([]crypto.KSAKey, number)
    
    	cryptoAlgo := crypto.NewAES()
    	for i := 0; i < number; i++ {
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		ksaKey, akmsKSAKey, err := generateNewKSAKey(cryptoAlgo, platformKey.Value)
    
    		if err != nil {
    			log.Error(err)
    			return err
    		}
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		ksaKeys[i] = ksaKey
    		akmsKSAKeys[i] = *akmsKSAKey
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	remoteKMSAdrress := fmt.Sprintf("%s:%d", remoteKMS.Address, remoteKMS.Port)
    
    	err = kms.sendKSAKeysToPlatformKmsPeer(remoteKMSAdrress, platformKey.Id.String(), requestID, ksaKeys)
    
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    
    	// Use the real processID when we know what it is
    
    	err = kms.ckmsAkmsClient.SendKSAKeysToRequestingInstances(requestID, platformKey.ProcessId, akmsKSAKeys)
    
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    
    
    func (kms *KMS) EventBus() *event.EventBus {
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    // TODO/XXX error handling.
    
    func (kms *KMS) RemovePeer(kmsPeerSocket string) error {
    	_, err := kms.FindPeerById(kmsPeerSocket)
    	if err != nil {
    		return fmt.Errorf("Could not remove peer with id: %s, not found", kmsPeerSocket)
    
    Malte Bauch's avatar
    Malte Bauch committed
    	}
    
    
    	kms.kmsPeersMutex.Lock()
    	defer kms.kmsPeersMutex.Unlock()
    	// peer.quit <- true
    	delete(kms.KmsPeers, kmsPeerSocket)
    
    	return nil
    
    func (kms *KMS) FindPeerById(id string) (*peers.KmsPeer, error) {
    	kms.kmsPeersMutex.RLock()
    	defer kms.kmsPeersMutex.RUnlock()
    
    	peer, ok := kms.KmsPeers[id]
    	if !ok {
    		return nil, fmt.Errorf("No peer found for id: %s", id)
    
    	return peer, nil
    
    func (kms *KMS) RoutingTableDeepCopy() map[uuid.UUID]*Route {
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    	routingTableCopy := make(map[uuid.UUID]*Route, len(kms.KmsPeers))
    
    	for k, v := range kms.routingTable {
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    		routingTableCopy[k] = v
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    	return routingTableCopy
    
    func (kms *KMS) PeersDeepCopy() map[string]*peers.KmsPeer {
    	peersCopy := make(map[string]*peers.KmsPeer, len(kms.KmsPeers))
    
    	kms.kmsPeersMutex.RLock()
    	defer kms.kmsPeersMutex.RUnlock()
    
    	for k, v := range kms.KmsPeers {
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    		peersCopy[k] = v
    
    Neil-Jocelyn Schark's avatar
    Neil-Jocelyn Schark committed
    	return peersCopy
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    
    func resolveHostnameToIPForQuantumModule(peer *config.Peer) error {
    	const connectionRetries = 60
    	var ipAddr []net.IP
    	var err error
    
    	// If the address is not set, try to resolve the hostname.
    	if peer.QuantumModule.Address == "" && peer.QuantumModule.Hostname != "" {
    		log.Info("Trying to get IP from hostname for quantum module: ", peer.QuantumModule.Hostname)
    		for j := 0; j < connectionRetries; j++ {
    			ipAddr, err = net.LookupIP(peer.QuantumModule.Hostname)
    			if err == nil {
    				break
    			}
    			log.Errorf("Failed to get IP from hostname %s, retrying in 2 seconds (attempt %d/%d)", peer.QuantumModule.Hostname, j+1, connectionRetries)
    			time.Sleep(2 * time.Second)
    		}
    		if err != nil {
    			return fmt.Errorf("IP address not set and failed to resolve hostname for two minutes for quantum module: %s. Error: %s", peer.QuantumModule.Hostname, err.Error())
    		}
    
    		// Just use the first valid IP for now.
    		ipAdrrString := ipAddr[0].String()
    
    		log.Infof("Resolved hostname to IP address for quantum module. Hostname: %s, IP: %s", peer.QuantumModule.Hostname, ipAdrrString)
    		peer.QuantumModule.Address = ipAdrrString
    	} else if peer.QuantumModule.Address == "" && peer.QuantumModule.Hostname == "" {
    		return fmt.Errorf("IP address and hostname not set for quantum module. Erros may occur and the module might not work properly.")
    	}
    
    	return nil
    }
    
    
    func generateNewKSAKey(cryptoAlgo crypto.CryptoAlgorithm, platformKeyValue []byte) (*pbIC.Key, *crypto.KSAKey, error) {
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	// generate ksa key
    	ksaKeyId := uuid.New()
    	ksaKey, err := crypto.Random256BitKey()
    	if err != nil {
    		log.Error(err)
    		return nil, nil, err
    	}
    
    	// encrypt the key
    	nonce, encryptedKSAKey, err := cryptoAlgo.Encrypt(ksaKey.Key, platformKeyValue)
    	if err != nil {
    		log.Error(err)
    		return nil, nil, err
    	}
    
    	ksaKeyAsString := base64.StdEncoding.EncodeToString(ksaKey.Key)
    	encryptedKSAKeyAsString := base64.StdEncoding.EncodeToString(encryptedKSAKey)
    	nonceAsString := base64.StdEncoding.EncodeToString(nonce)
    
    	ksaKeyToSend := &pbIC.Key{
    		Id:    ksaKeyId.String(),
    		Nonce: nonceAsString,
    		Key:   encryptedKSAKeyAsString,
    	}
    
    
    	akmsKSAKey := &crypto.KSAKey{
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		KeyID: ksaKeyId.String(),
    		Key:   ksaKeyAsString,
    	}
    
    	return ksaKeyToSend, akmsKSAKey, nil
    }
    
    func (kms *KMS) sendKSAKeysToPlatformKmsPeer(kmsPeerAddress, platformKeyID, requestID string, ksaKeys []*pbIC.Key) error {
    	gRPCTransportCreds, err := kmstls.GenerateGRPCClientTransportCredsBasedOnTLSFlag(kms.tlsConfig)
    	if err != nil {
    		return fmt.Errorf("unable to generate gRPC transport creds: %w", err)
    	}
    
    
    	remoteConn, err := grpc.NewClient(kmsPeerAddress, grpc.WithTransportCredentials(gRPCTransportCreds))
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    	remoteClient := pbIC.NewKmsTalkerClient(remoteConn)
    
    
    	ctx, cancel := context.WithTimeout(context.Background(), kms.gRPCTimeout)
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	// create a new context with some metadata
    	md := metadata.Pairs("hostname", kms.kmsName)
    	ctx = metadata.NewOutgoingContext(ctx, md)
    	defer cancel()
    	_, err = remoteClient.KeyDelivery(ctx, &pbIC.KeyDeliveryRequest{
    		KeyId:     platformKeyID,
    		RequestId: requestID,
    		KmsId:     kms.kmsUUID.String(),
    		Keys:      ksaKeys,
    	})
    	if err != nil {
    		log.Error(err)
    		return err
    	}
    
    	return nil
    }
    
    func (kms *KMS) exchangeKeyAfterETSI14GetKeyRequest(receivingCKMSID uuid.UUID, number int64, requestID string) ([]crypto.KSAKey, error) {
    
    	// CreateRouteRequest
    	pathID := uuid.New()
    	log.Debugf("(ETSI14) created new path id: %s, for incoming ETSI14 key request", pathID)
    
    	receiverChan, err := kms.receiver.RequestReceiverChannel(pathID)
    	if err != nil {
    		log.Error(err)
    		return nil, err
    	}
    
    	err = kms.eventBus.Publish(event.NewCreateRouteEvent(pathID.String(), receivingCKMSID.String()))
    	if err != nil {
    		log.Errorf("(ETSI14) Failed sending a create route request: %s", err)
    		return nil, err
    	}
    
    	select {
    	case <-receiverChan:
    	case <-time.After(20 * time.Second):
    		if err := kms.receiver.RemoveReceiver(pathID); err != nil {
    			log.Errorf("Failed removing receiver for pathId: %s ; err: %v", pathID, err)
    		}
    		log.Errorf("(ETSI14) Platform Key exchange failed for PathID: %s", pathID)
    		return nil, fmt.Errorf("Platform Key exchange failed for PathID: %s", pathID)
    	}
    
    	// GenerateAndSend
    
    	return kms.generateAndReturnKsaKey(receivingCKMSID, pathID, number, requestID)
    
    func (kms *KMS) generateAndReturnKsaKey(receivingCKMSID, pathID uuid.UUID, number int64, requestID string) ([]crypto.KSAKey, error) {
    
    	if number < 1 {
    		log.Errorf("number must be positive and at least 1, provided: %d\n", number)
    		return nil, fmt.Errorf("number must be positive and at least 1, provided: %d", number)
    	}
    
    	log.Infof("KMS store: %v", kms.remoteKMSMapping)
    	remoteKMS, err := kms.GetRemoteKMS(receivingCKMSID.String())
    	if err != nil {
    		log.Error(err)
    		return nil, err
    	}
    
    	platformKey, err := kms.GetSpecificPlatformKey(receivingCKMSID.String(), pathID)
    	if err != nil {
    		log.Error(err)
    		return nil, err
    	}
    
    	ksaKeysToSendToRemoteKMS := make([]*pbIC.Key, number)
    
    	ksaKeysToReturn := make([]crypto.KSAKey, number)
    
    	cryptoAlgo := crypto.NewAES()
    	for i := int64(0); i < number; i++ {
    		remoteKSAKey, localKSAKey, err := generateNewKSAKey(cryptoAlgo, platformKey.Value)
    		if err != nil {
    			log.Error(err)
    			return nil, err
    		}
    
    		ksaKeysToReturn[i] = *localKSAKey
    		ksaKeysToSendToRemoteKMS[i] = remoteKSAKey
    	}
    
    	remoteKMSAdrress := fmt.Sprintf("%s:%d", remoteKMS.Address, remoteKMS.Port)
    
    
    	err = kms.sendKSAKeysToPlatformKmsPeer(remoteKMSAdrress, platformKey.Id.String(), requestID, ksaKeysToSendToRemoteKMS)
    
    	if err != nil {
    		log.Error(err)
    		return nil, err
    	}
    
    	return ksaKeysToReturn, nil
    }
    
    
    func (kms *KMS) GetID() uuid.UUID {
    	return kms.kmsUUID
    }