diff --git a/internal/kms/kms-keystore.go b/internal/kms/kms-keystore.go new file mode 100644 index 0000000000000000000000000000000000000000..56d3f74b324bc40146f3a5af94a393f9ab53e451 --- /dev/null +++ b/internal/kms/kms-keystore.go @@ -0,0 +1,69 @@ +package kms + +import ( + "crypto/sha256" + "errors" + "fmt" + "log" + "sync" + + "code.fbi.h-da.de/m.stiemerling/proto-kms/quantumlayer" +) + +type kmsKS interface { + KeyChopper256Bit(bulkKey *quantumlayer.QuantumLayerBulkKey) (err error) + addKey(int64, [8]byte) +} + +// holds a single ready to use 256 bit key +type kmsKSElement struct { + keyID string + key []byte // a 256 bit key +} + +type kmsKeyStore struct { + keyStoreMutex sync.Mutex + keyStore map[string]kmsKSElement +} + +func (ks *kmsKeyStore) addKey(bulkKeyId int64, keyToadd []byte) { + newKeyElement := kmsKSElement{} + + //generate keyID out of bulkKeyId and has of keyToadd + h := sha256.New() + hasofkey := string(h.Sum(keyToadd[:])) + newKeyElement.keyID = fmt.Sprintf("%x.%x", bulkKeyId, hasofkey) + newKeyElement.key = keyToadd + + ks.keyStoreMutex.Lock() + defer ks.keyStoreMutex.Unlock() + // test for collisions + if _, notThere := ks.keyStore[newKeyElement.keyID]; notThere { + log.Fatalf("Whop: addKey collission of key id %s for bulkKeyID %d", newKeyElement.keyID, bulkKeyId) + return + } + // ok to add + ks.keyStore[newKeyElement.keyID] = newKeyElement + +} + +// Takes a bulk of keys and chops them in 256bit keys each +// Any remainder is discarded +func (ks *kmsKeyStore) KeyChopper256Bit(bulkKey *quantumlayer.QuantumLayerBulkKey) (err error) { + if bulkKey.BulkKeyLength != len(*bulkKey.BulkKey) { + err = errors.New("bulkKey length mismatch") + return err + } + + // Let's chop! + key := *bulkKey.BulkKey + for len(key) > 8 { + tmpkey := key[:8] + ks.addKey(bulkKey.BulkKeyId, tmpkey) + // shorten the key storage + key = key[8:] + + } + return nil + +} diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 586d8374c0e8a31d648f3edf467e9498cfe5a012..68a85dd32e448d66274837b261324f81feb6af2c 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -5,53 +5,132 @@ 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 + GlobalKeyHandler(time.Duration) error } -type qlElementId uuid.UUID +type qlElementId uint64 type qlElementLinkID int // The general emulated KMS type eKMS struct { + kmsName string + kmsUUID uuid.UUID + qleMapMutex sync.Mutex QuantumElements map[qlElementId]*QuantumElement - KeyStorages map[qlElementId]map[qlElementLinkID]uint64 } // 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 { - qlElementName string // the name of this Quantum Element - qlElementUUID uuid.UUID // the uuid for this device - QlElement *quantumlayer.QuantumlayerEmuPRNG // the actual quantum element - QuantumElementLinks map[qlElementLinkID]*quantumlayer.QuantumLayer // contains information about the quantum links + 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() (newekms *eKMS) { +func NeweKMS(kmsName string, kmsUUID uuid.UUID) (newekms *eKMS) { return &eKMS{ + kmsName: kmsName, + kmsUUID: kmsUUID, QuantumElements: make(map[qlElementId]*QuantumElement), - KeyStorages: make(map[qlElementId]map[qlElementLinkID]uint64), } } -func (kms *eKMS) AddQuantumElement(kmsName string, kmsUUID uuid.UUID, kmsUDPAddrr string) *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{ - qlElementName: kmsName, - qlElementUUID: kmsUUID, - QlElement: ql, - QuantumElementLinks: make(map[qlElementLinkID]*quantumlayer.QuantumLayer), + QuantumElementLink: ql, + keyStoreLocal: &ksl, + keyStoreRemote: &ksr, } - kms.QuantumElements[qlElementId(kmsUUID)] = &qle + // 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(&bulkKeysLocal) + } + } + 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.... + } +} diff --git a/internal/main.go b/internal/main.go index eb47a37eebba58c640d327b38b25bc7285901f37..eaee39da2650297dda71c8ce1feeddff8cd4aacf 100644 --- a/internal/main.go +++ b/internal/main.go @@ -4,6 +4,7 @@ import ( "flag" "log" "net" + "time" "code.fbi.h-da.de/m.stiemerling/proto-kms/kms" "github.com/google/uuid" @@ -56,10 +57,10 @@ func main() { func emulatedKMS(myName string, myUDPAddr string, peerUDPAddr string) { // Attach to eKMS - emuKMS := kms.NeweKMS() + emuKMS := kms.NeweKMS(myName, uuid.New()) // Fire up Quantum LinK - myQL := emuKMS.AddQuantumElement(myName, uuid.New(), myUDPAddr) + myQL := emuKMS.AddQuantumElement(myUDPAddr) udpQL2Addr, err := net.ResolveUDPAddr("udp", peerUDPAddr) if err != nil { @@ -67,18 +68,8 @@ func emulatedKMS(myName string, myUDPAddr string, peerUDPAddr string) { return } - myQL.QlElement.AddPeer(*udpQL2Addr) + myQL.QuantumElementLink.AddPeer(*udpQL2Addr) - for { - resultQLLocal, err := myQL.QlElement.GetKeyBatchLocal() - if err == nil { - log.Printf("Current key IDs are for %s local %d:", myName, resultQLLocal.BulkKeyId) - - } - resultQLRemote, err := myQL.QlElement.GetKeyBatchPeer() - if err == nil { - log.Printf("Current key IDs are for remote %d:", resultQLRemote.BulkKeyId) - - } - } + // TODO/XXX catch errors! + emuKMS.GlobalKeyHandler(7 * time.Second) }