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 (
"crypto/rand"
"log"
"math/big"
"sync"
"time"
"code.fbi.h-da.de/m.stiemerling/proto-kms/quantumlayer"
"github.com/google/uuid"
)
type Qkdnkms interface {
AddQuantumElement() *QuantumElement
AddPeer(kmsPeerSocket string)
RemovePeer(kmsPeerSocket string)
type qlElementLinkID int
// The general emulated KMS
type eKMS struct {
kmsName string
kmsUUID uuid.UUID
qleMapMutex sync.Mutex
QuantumElements map[qlElementId]*QuantumElement
KmsPeers map[string]*kmsPeer
}
// 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 QuantumElement struct {
qlID qlElementId
QuantumElementLink *quantumlayer.QuantumlayerEmuPRNG // contains information about the quantum links
//key stores go here
keyStoreLocal *kmsKeyStore // the keys this local entity has produced and are ready to use
keyStoreRemote *kmsKeyStore // the keys th remote entity (peer) has produced and are ready to use
func NeweKMS(kmsName string, kmsUUID uuid.UUID) (newekms *eKMS) {
return &eKMS{
kmsName: kmsName,
kmsUUID: kmsUUID,
QuantumElements: make(map[qlElementId]*QuantumElement),
}
}
func (kms *eKMS) AddQuantumElement(kmsUDPAddrr string) *QuantumElement {
//Get an emulated Quantumlayer
ql := quantumlayer.NewQuantumlayerEmuPRNG()
ql.PowerOn(kmsUDPAddrr)
ksl := kmsKeyStore{
keyStore: make(map[string]kmsKSElement),
}
ksr := kmsKeyStore{
keyStore: make(map[string]kmsKSElement),
}
qle := QuantumElement{
QuantumElementLink: ql,
keyStoreLocal: &ksl,
keyStoreRemote: &ksr,
// generate a ID for this quantum element that is unique locally
var randError error
qle.qlID, randError = kms.GenerateNewQleID()
if randError != nil {
log.Fatalf("GenerateNewQleID: %s", randError)
return nil
}
kms.QuantumElements[qle.qlID] = &qle
return &qle
}
func (kms *eKMS) GlobalKeyHandler(waitTime time.Duration) error {
// periodically walk through QuantumElements and retrieve their
// - local key bulk buffer
// - remote key bulk buffer
// feed this into the corresponding key buffers of the kmss
for {
for currentQE := range kms.QuantumElements {
log.Printf("%s GlobalKeyHandler reading...\n", kms.kmsName)
bulkKeysLocal, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBatchLocal()
if err != nil {
log.Printf("%s failed to retrieve local bulkkeys with error %s", kms.kmsName, err)
} else {
// process bulkKeysLocal
log.Printf("%s produced %d bytes of key locally", kms.kmsName, bulkKeysLocal.BulkKeyLength)
kms.QuantumElements[currentQE].keyStoreLocal.KeyChopper256Bit(&bulkKeysLocal)
}
bulkKeysRemote, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBatchPeer()
if err != nil {
log.Printf("%s failed to retrieve remote bulkkeys with error %s", kms.kmsName, err)
} else {
// process bulkKeysRemote
log.Printf("%s received %d bytes of key from remote peer", kms.kmsName, bulkKeysRemote.BulkKeyLength)
kms.QuantumElements[currentQE].keyStoreRemote.KeyChopper256Bit(&bulkKeysRemote)
}
}
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() (qlElementId, error) {
for { // this needs a condiction to stop!
bigRand, randError := rand.Int(rand.Reader, big.NewInt(100000))
if randError != nil {
return 0, randError
}
propopsedQlElementID := qlElementId(bigRand.Uint64())
// check if ID is already taken
if kms.QuantumElements[propopsedQlElementID] == nil {
return propopsedQlElementID, nil
}
//keep going....
}
}