Skip to content
Snippets Groups Projects
kms.go 8.26 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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 (
    
    	"crypto/rand"
    
    	"encoding/binary"
    
    	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"
    	"code.fbi.h-da.de/danet/proto-kms/quantumlayer"
    
    	"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() *QuantumElement
    
    	GlobalKeyHandler(time.Duration) error
    
    	AddPeer(kmsPeerSocket string, servingQLE *QuantumElement)
    
    	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"
    )
    
    type EKMS struct {
    
    	kmsName             string
    	kmsUUID             uuid.UUID
    	interComAddr        string
    	qleMapMutex         sync.Mutex
    	QuantumElements     map[uint32]*QuantumElement
    	externalNotifierQLE chan uint32
    	kmsPeersMutex       sync.Mutex
    	// TODO(maba): find a better name for this
    	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
    }*/
    
    
    	QuantumElementLink *quantumlayer.QuantumlayerEmuPRNG // contains information about the quantum links
    
    	//key stores of unchopped bulk keys go here
    	rawBulkKeysMutex sync.Mutex
    
    	rawBulkKeys      map[int64]*quantumlayer.QuantumLayerBulkKey
    
    	keyStorePeer     *kmsKeyStore // the keys used between two peers.
    
    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,
    
    		QuantumElements:         make(map[uint32]*QuantumElement),
    
    		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(kmsUDPAddrr string, generateKeys bool, logOutput io.Writer, logLevel log.Level, logInJson bool) *QuantumElement {
    
    	ql := quantumlayer.NewQuantumlayerEmuPRNG(logOutput, logLevel, logInJson)
    
    	ql.Configure(kmsUDPAddrr)
    
    	ql.PowerOn(generateKeys)
    
    		QuantumElementLink: ql,
    
    		rawBulkKeys:        make(map[int64]*quantumlayer.QuantumLayerBulkKey),
    
    		// keyStorePeer:   not set, will be set later on, if key size is negotiated.
    
    	// generate a ID for this quantum element that is unique locally
    	var randError error
    
    	qle.QlID, randError = kms.GenerateNewQleID()
    
    	if randError != nil {
    
    		log.Fatalf("%s: GenerateNewQleID: %s", kms.kmsName, randError)
    
    	kms.QuantumElements[qle.QlID] = &qle
    
    // TODO: Name of this function is misleading, as it only reads the bulk keys, but nothing more else
    
    func (kms *EKMS) GlobalKeyHandler(waitTime time.Duration) error {
    
    	// periodically walk through QuantumElements and retrieve the
    	// - key bulk buffer for each peer
    
    	// feed this into the corresponding key buffers of the kmss
    	for {
    		for currentQE := range kms.QuantumElements {
    
    			log.Debugf("%s GlobalKeyHandler reading...\n", kms.kmsName)
    
    			bulkKeys, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBulkPeer()
    
    			if err != nil {
    
    				log.Errorf("%s failed to retrieve bulkkeys with error %s", kms.kmsName, err)
    
    				// Add to the slice, but not process yet.
    				log.Debugf("%s produced %d bytes of key", kms.kmsName, bulkKeys.BulkKeyLength)
    				kms.QuantumElements[currentQE].rawBulkKeysMutex.Lock()
    
    				//kms.QuantumElements[currentQE].rawBulkKeys = append(kms.QuantumElements[currentQE].rawBulkKeys, &bulkKeys)
    				kms.QuantumElements[currentQE].rawBulkKeys[bulkKeys.BulkKeyId] = &bulkKeys
    
    				kms.QuantumElements[currentQE].rawBulkKeysMutex.Unlock()
    
    				//kms.QuantumElements[currentQE].keyStorePeer.KeyChopper256Bit(&bulkKeys)
    
    		// TODO: Better approach required than a sleep timer!
    
    		time.Sleep(waitTime)
    	}
    }
    
    // This has a design flaw, as the generated ID is returned to the calling function and used there.
    // However, when being used a potential other caller might received the same qlElementId
    // TODO/XXX: This would be collision and must be eventually avoided
    
    func (kms *EKMS) GenerateNewQleID() (uint32, error) {
    
    	for { // this needs a condition to stop!
    
    		// create buffer for uint32, so reserve 4 bytes
    		buf := make([]byte, 4)
    
    		// fill the buffer from rand
    		_, err := rand.Read(buf)
    		if err != nil {
    			return 0, err
    
    		propopsedQlElementID := binary.BigEndian.Uint32(buf)
    
    
    		// check if ID is already taken
    		if kms.QuantumElements[propopsedQlElementID] == nil {
    			return propopsedQlElementID, nil
    		}
    		//keep going....
    	}
    }
    
    func (kms *EKMS) AddPeer(kmsPeerSocket string, servingQLE *QuantumElement) (*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(servingQLE, kmsPeerSocket, kms.externalNotifierKMSPeer)
    	if err != nil {
    		return nil, err
    
    	}
    	peer.tcpSocketStr = kmsPeerSocket
    
    	kms.kmsPeersMutex.Lock()
    
    	kms.KmsPeers[kmsPeerSocket] = peer
    
    	//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) {
    
    Malte Bauch's avatar
    Malte Bauch committed
    	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)
    
    Malte Bauch's avatar
    Malte Bauch committed
    	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
    }