Newer
Older
// 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 (
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"
"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
AddPeer(kmsPeerSocket string, servingQLE *QuantumElement)
RemovePeer(kmsPeerSocket string)
type Route struct {
Previous *kmsPeer
Next *kmsPeer
}
type BitKeyLength string
const (
BitKeyLen128 BitKeyLength = "128"
BitKeyLen256 BitKeyLength = "256"
BitKeyLen512 BitKeyLength = "512"
)
// The general emulated KMS
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
}*/
type QuantumElement struct {
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)
}
kmsName: kmsName,
kmsUUID: kmsUUID,
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 {
//Get an emulated Quantumlayer
ql := quantumlayer.NewQuantumlayerEmuPRNG(logOutput, logLevel, logInJson)
qle := QuantumElement{
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()
log.Fatalf("%s: GenerateNewQleID: %s", kms.kmsName, randError)
kms.QuantumElements[qle.QlID] = &qle
return &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()
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.kmsPeersMutex.Unlock()
//go peer.PeerHandler(kms.kmsName)
return peer, nil
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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)
func (kms *EKMS) AddExternalNotifierQLE(in chan uint32) {
kms.externalNotifierQLE = in
}
func (kms *EKMS) AddExternalNotifierKMSPeer(in chan string) {
kms.externalNotifierKMSPeer = in
}