Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
kms.go 5.38 KiB
// This package kms implements a simplistic key managment 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"
	"sync"

	log "github.com/sirupsen/logrus"

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

//type Qkdnkms interface {
//	//AddExternalNotifierGeneral(chan bool)   // used to indicate unspecific changes
//	AddExternalNotifierQLE(chan uint32)     // used to indicate changes to specific Quantum Link Element (QLE)
//	AddExternalNotifierKMSPeer(chan string) // used to indicate changes to specific KMSPeer
//	AddQuantumElement() *EmulatedQuantumModule
//	GlobalKeyHandler(time.Duration) error
//	AddPeer(kmsPeerSocket string, servingQLE *EmulatedQuantumModule)
//	RemovePeer(kmsPeerSocket string)
//	FindPeerUuid(uuid.UUID) *kmsPeer
//}

type Route struct {
	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
	qleMapMutex         sync.Mutex
	quantumModules      map[uuid.UUID]QuantumModule
	quantumModulesMutex sync.RWMutex
	externalNotifierQLE chan uint32
	kmsPeersMutex       sync.Mutex
	// TODO(maba): find a better name for this
	// TODO: add mutex
	keysForPathId           map[uuid.UUID]string
	routingTable            map[uuid.UUID]*Route
	KmsPeers                map[string]*kmsPeer
	externalNotifierKMSPeer chan string
	pbETSI.UnimplementedKmsETSIServer
	pbIC.UnimplementedKmsTalkerServer
	supportedKeyLengths map[BitKeyLength]bool
}

// 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[uuid.UUID]QuantumModule),
		routingTable:            make(map[uuid.UUID]*Route),
		KmsPeers:                make(map[string]*kmsPeer),
		externalNotifierQLE:     nil, // just be surely set to nil!
		externalNotifierKMSPeer: nil, // just be surely set to nil!
		supportedKeyLengths:     make(map[BitKeyLength]bool),
	}

	createdEKMS.supportedKeyLengths[BitKeyLen256] = true

	// start the inter communication gRPC server
	go StartInterComm(interComAddr, createdEKMS)

	return createdEKMS
}

func (kms *EKMS) AddQuantumElement(qm QuantumModule) error {
	kms.quantumModulesMutex.Lock()
	kms.quantumModules[qm.ID()] = 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.externalNotifierKMSPeer)
	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 = ", 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)
		}
	}

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

	return nil
}

// 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)
	return
}

func (kms *EKMS) AddExternalNotifierQLE(in chan uint32) {
	kms.externalNotifierQLE = in
}

func (kms *EKMS) AddExternalNotifierKMSPeer(in chan string) {
	kms.externalNotifierKMSPeer = in
}

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
}