diff --git a/internal/api/gen/proto/go/kmsetsi/kmsetsiproto.pb.go b/internal/api/gen/proto/go/kmsetsi/kmsetsiproto.pb.go index f859f18b5db223fd1a89169290cb840d05770439..69adc0800285ac2fb4ff2ad07a5d5feb0febc8ac 100644 --- a/internal/api/gen/proto/go/kmsetsi/kmsetsiproto.pb.go +++ b/internal/api/gen/proto/go/kmsetsi/kmsetsiproto.pb.go @@ -551,7 +551,7 @@ type ETSIGetEncryptKeys256BitReply struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - KeyID string `protobuf:"bytes,1,opt,name=keyID,proto3" json:"keyID,omitempty"` + KeyID uint64 `protobuf:"varint,1,opt,name=keyID,proto3" json:"keyID,omitempty"` Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` } @@ -587,11 +587,11 @@ func (*ETSIGetEncryptKeys256BitReply) Descriptor() ([]byte, []int) { return file_kmsetsi_kmsetsiproto_proto_rawDescGZIP(), []int{11} } -func (x *ETSIGetEncryptKeys256BitReply) GetKeyID() string { +func (x *ETSIGetEncryptKeys256BitReply) GetKeyID() uint64 { if x != nil { return x.KeyID } - return "" + return 0 } func (x *ETSIGetEncryptKeys256BitReply) GetKey() []byte { @@ -653,7 +653,7 @@ var file_kmsetsi_kmsetsiproto_proto_rawDesc = []byte{ 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x47, 0x0a, 0x1d, 0x45, 0x54, 0x53, 0x49, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x32, 0x35, 0x36, 0x42, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x10, 0x0a, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x32, 0xb7, 0x04, 0x0a, 0x07, 0x4b, 0x6d, 0x73, 0x45, 0x54, 0x53, 0x49, 0x12, 0x56, 0x0a, 0x10, 0x45, 0x54, 0x53, 0x49, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, diff --git a/internal/api/kmsetsi/kmsetsi/kmsetsiproto.proto b/internal/api/kmsetsi/kmsetsi/kmsetsiproto.proto index ae04fa50d55d399bacde166c56242428773e860b..9f0cc1e77236d1435a4bf1c21b129256ffd0ec15 100644 --- a/internal/api/kmsetsi/kmsetsi/kmsetsiproto.proto +++ b/internal/api/kmsetsi/kmsetsi/kmsetsiproto.proto @@ -65,12 +65,12 @@ message ETSIGetEncryptKeys256BitRequest { /* out kms-keystore.go * type kmsKSElement struct { - * keyID string + * keyID uint64 * key []byte // a 256 bit key * } */ message ETSIGetEncryptKeys256BitReply { - string keyID = 1; + uint64 keyID = 1; bytes key = 2; } diff --git a/internal/kms/kms-keystore.go b/internal/kms/kms-keystore.go index c3741054388084247fd318fac25f3de08bbe63a4..8ba83293b298320482f9d5ef47e74d57f9847707 100644 --- a/internal/kms/kms-keystore.go +++ b/internal/kms/kms-keystore.go @@ -2,7 +2,6 @@ package kms import ( "errors" - "fmt" "sync" log "github.com/sirupsen/logrus" @@ -15,29 +14,39 @@ type kmsKS interface { addKey(int64, [8]byte) } -// holds a single ready to use 256 bit key +// holds a single ready to bit key, length can be configured type kmsKSElement struct { - keyID string - key []byte // a 256 bit key + keyID uint64 + key []byte // a key } type kmsKeyStore struct { - keyStoreMutex sync.Mutex - keyStore map[string]*kmsKSElement + keyStoreMutex sync.Mutex + underlyingBulkId int64 + keyStore map[uint64]*kmsKSElement + keySingleSize uint // the size of a single key, given as unit of bits } -func (ks *kmsKeyStore) addKey(bulkKeyId int64, keyToadd []byte) { +func NewKmsKeyStore(desiredkeySingleSizeLength uint) kmsKeyStore { + return kmsKeyStore{ + underlyingBulkId: 0, + keyStore: make(map[uint64]*kmsKSElement), + keySingleSize: desiredkeySingleSizeLength, + } +} + +func (ks *kmsKeyStore) addKey(keyId uint64, keyToadd []byte) { newKeyElement := kmsKSElement{} //generate keyID out of bulkKeyId and has of keyToadd - newKeyElement.keyID = fmt.Sprintf("%x.%x", bulkKeyId, keyToadd) + newKeyElement.keyID = keyId newKeyElement.key = keyToadd ks.keyStoreMutex.Lock() defer ks.keyStoreMutex.Unlock() // test for collisions if _, notThere := ks.keyStore[newKeyElement.keyID]; notThere { - log.Errorf("Whop: addKey collission of key id %s for bulkKeyID %d", newKeyElement.keyID, bulkKeyId) + log.Errorf("Whop: addKey collission of key id %s for bulkKeyID %d", newKeyElement.keyID, ks.underlyingBulkId) return } // ok to add @@ -45,21 +54,35 @@ func (ks *kmsKeyStore) addKey(bulkKeyId int64, keyToadd []byte) { } -// Takes a bulk of keys and chops them in 256bit keys each +// Takes a bulk of keys and chops them in chopFactor keys each // Any remainder is discarded -func (ks *kmsKeyStore) KeyChopper256Bit(bulkKey *quantumlayer.QuantumLayerBulkKey) (err error) { +func (ks *kmsKeyStore) KeyChopper(bulkKey *quantumlayer.QuantumLayerBulkKey) (err error) { + if ks.keySingleSize == 0 { + err = errors.New("KeyChopper: no keySingleSize set") + return err + } + // TODO check if multiple of 8 (1 Byte) + if bulkKey.BulkKeyLength != len(*bulkKey.BulkKey) { err = errors.New("bulkKey length mismatch") return err } + // Store underlying bulkID + ks.underlyingBulkId = bulkKey.BulkKeyId + + var keyId uint64 + keyId = 0 + // Let's chop! + chopFactor := ks.keySingleSize >> 3 key := *bulkKey.BulkKey - for len(key) > 32 { - tmpkey := key[:32] - ks.addKey(bulkKey.BulkKeyId, tmpkey) + for len(key) > int(chopFactor) { + tmpkey := key[:chopFactor] + ks.addKey(keyId, tmpkey) + keyId++ // shorten the key storage - key = key[32:] + key = key[chopFactor:] } return nil } diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 8c165d61ba90c56272348c3058af818ee123546f..e76e079a98f1cabd90db593e6edd487bb0eae36b 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -30,7 +30,13 @@ type Qkdnkms interface { FindPeerUuid(uuid.UUID) *kmsPeer } -type qlElementLinkID int +type BitKeyLength string + +const ( + BitKeyLen128 BitKeyLength = "128" + BitKeyLen256 BitKeyLength = "256" + BitKeyLen512 BitKeyLength = "512" +) // The general emulated KMS type EKMS struct { @@ -44,6 +50,7 @@ type EKMS struct { externalNotifierKMSPeer chan string pbETSI.UnimplementedKmsETSIServer pbIC.UnimplementedKmsTalkerServer + supportedKeyLengths map[BitKeyLength]bool } // Will keep information about the quantum elements that this EKMS is talking to @@ -57,9 +64,10 @@ type QuantumElementInterface interface { type QuantumElement struct { QlID uint32 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 + //key stores of unchopped bulk keys go here + rawBulkKeysMutex sync.Mutex + rawBulkKeys []*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) (newEKMS *EKMS) { @@ -84,14 +92,19 @@ func NewEKMS(kmsName string, kmsUUID uuid.UUID, logOutput io.Writer, logLevel lo log.SetReportCaller(false) } - return &EKMS{ + createdEKMS := EKMS{ kmsName: kmsName, kmsUUID: kmsUUID, QuantumElements: make(map[uint32]*QuantumElement), 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 + + return &createdEKMS } func (kms *EKMS) AddQuantumElement(kmsUDPAddrr string, generateKeys bool, logOutput io.Writer, logLevel log.Level, logInJson bool) *QuantumElement { @@ -101,17 +114,9 @@ func (kms *EKMS) AddQuantumElement(kmsUDPAddrr string, generateKeys bool, logOut ql.Configure(kmsUDPAddrr) ql.PowerOn(generateKeys) - ksl := kmsKeyStore{ - keyStore: make(map[string]*kmsKSElement), - } - ksr := kmsKeyStore{ - keyStore: make(map[string]*kmsKSElement), - } - qle := QuantumElement{ QuantumElementLink: ql, - keyStoreLocal: &ksl, - keyStoreRemote: &ksr, + // keyStorePeer: not set, will be set later on, if key size is negotiated. } // generate a ID for this quantum element that is unique locally @@ -127,33 +132,28 @@ func (kms *EKMS) AddQuantumElement(kmsUDPAddrr string, generateKeys bool, logOut 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 their - // - local key bulk buffer - // - remote key bulk buffer + // 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) - bulkKeysLocal, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBatchLocal() + bulkKeys, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBulkPeer() if err != nil { - log.Errorf("%s failed to retrieve local bulkkeys with error %s", kms.kmsName, err) + log.Errorf("%s failed to retrieve bulkkeys with error %s", kms.kmsName, err) } else { - // process bulkKeysLocal - log.Debugf("%s produced %d bytes of key locally", kms.kmsName, bulkKeysLocal.BulkKeyLength) - kms.QuantumElements[currentQE].keyStoreLocal.KeyChopper256Bit(&bulkKeysLocal) + // 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].rawBulkKeysMutex.Unlock() - } + //kms.QuantumElements[currentQE].keyStorePeer.KeyChopper256Bit(&bulkKeys) - bulkKeysRemote, err := kms.QuantumElements[currentQE].QuantumElementLink.GetKeyBatchPeer() - if err != nil { - log.Errorf("%s failed to retrieve remote bulkkeys with error %s", kms.kmsName, err) - } else { - // process bulkKeysRemote - log.Debugf("%s received %d bytes of key from remote peer", kms.kmsName, bulkKeysRemote.BulkKeyLength) - kms.QuantumElements[currentQE].keyStoreRemote.KeyChopper256Bit(&bulkKeysRemote) } } // TODO: Better approach required than a sleep timer! diff --git a/internal/kms/kmsetsi.go b/internal/kms/kmsetsi.go index 4cc3f1cf10378f2a3c96e49e13ea233d0ecec3ae..297ca3e926313fbeb2e049a9e642af114c0ae48f 100644 --- a/internal/kms/kmsetsi.go +++ b/internal/kms/kmsetsi.go @@ -105,8 +105,8 @@ func (es *etsiServer) ETSIGetEncryptKeys256Bit(ctx context.Context, in *pb.ETSIG var err error // NOTE: change change change! for _, qe := range es.handlingEkms.QuantumElements { - quantumElementLocalKeyStore := qe.keyStoreLocal - randomKey, err = randomItemFromMap[string, *kmsKSElement](quantumElementLocalKeyStore.keyStore, es.visitedKeys) + quantumElementLocalKeyStore := qe.keyStorePeer + randomKey, err = randomItemFromMap[uint64, *kmsKSElement](quantumElementLocalKeyStore.keyStore, es.visitedKeys) if err != nil { return nil, status.Errorf(codes.Internal, "%v", err) } diff --git a/internal/kms/kmsintercom.go b/internal/kms/kmsintercom.go index 807375b6729a8cde8b388948328ac91875a3559b..4a6843833864fda5374d2cf73ebe6308168f4747 100644 --- a/internal/kms/kmsintercom.go +++ b/internal/kms/kmsintercom.go @@ -16,9 +16,16 @@ type kmsTalkerServer struct { pb.UnimplementedKmsTalkerServer } +// This must somehow find out and agree to a specific key lenght func (s *kmsTalkerServer) InterComCapabilities(ctx context.Context, in *pb.InterComCapabilitiesRequest) (capReply *pb.InterComCapabilitiesReply, err error) { log.Debugf("Received: %v", in.GetMyKmsName()) + // TODO: Call to ksp := NewKmsKeyStore(<desired-size-of-each-key-in-bits) + // this to be stored in the serving QLE QuantumElement struct under keyStorePeer + // Further, the KMS peers have to agree on a ready-to-be-used keyBulk based on the bulkId + // This requires to go through the rawBulkKeys of type QuantumElement and lookup a bulkId both side do know + // Once agreed upon on keyBulk, this here has to call KeyChopper of the actual NewKmsKeyStore + return &pb.InterComCapabilitiesReply{ PeerKmsName: "whatever", }, nil diff --git a/internal/kms/kmspeers.go b/internal/kms/kmspeers.go index a889a4470e87bfa7366de907371610fe3d090ca7..da559860cde9049c792ec32b6da7dcbb906f0878 100644 --- a/internal/kms/kmspeers.go +++ b/internal/kms/kmspeers.go @@ -29,6 +29,7 @@ type kmsPeerInfo interface { GetKmsPeerStatus() KmsPeerStatus GetKmsPeerId() uuid.UUID GetKmsPeerQkdiId() uint32 + KmsPeerKeyInit() } type kmsPeer struct { @@ -112,8 +113,8 @@ func (ph *kmsPeer) PeerHandler(kmsName string) { return } - // check if key is in remoteKeyStore - if key, ok := ph.servingQLE.keyStoreRemote.keyStore[encryptKeyRequest.KeyID]; ok { + // check if key is in KeyStore + if key, ok := ph.servingQLE.keyStorePeer.keyStore[encryptKeyRequest.KeyID]; ok { keyAsString := base64.StdEncoding.EncodeToString(key.key) log.Debugf("Agreed Key: %s", keyAsString) diff --git a/internal/main_test.go b/internal/main_test.go index ecae85d3e10b108f89b80dc8df2dcd58db1876ab..af1d98a32779e6426c4ef9761616f107383a4fb5 100644 --- a/internal/main_test.go +++ b/internal/main_test.go @@ -53,22 +53,20 @@ func TestMain(m *testing.M) { if selfTesting == true { log.Infof("%s in self-testing mode", ql1Name) - go emulatedKMS(ql2Name, udpQL2AddrString, udpQL1AddrString) - emulatedKMS(ql1Name, udpQL1AddrString, udpQL2AddrString) + go emulatedKMS(ql2Name, udpQL2AddrString, udpQL1AddrString, true) + emulatedKMS(ql1Name, udpQL1AddrString, udpQL2AddrString, false) } else { - log.Infof("%s in regular operations mode", ql1Name) - emulatedKMS(ql1Name, udpQL1AddrString, udpQL2AddrString) + log.Infof("%s in regular mode of operation", ql1Name) + emulatedKMS(ql1Name, udpQL1AddrString, udpQL2AddrString, false) } - - return } -func emulatedKMS(myName string, myUDPAddr string, peerUDPAddr string) { +func emulatedKMS(myName string, myUDPAddr string, peerUDPAddr string, generatedKeys bool) { // Attach to eKMS - emuKMS := kms.NewEKMS(myName, uuid.New(), os.Stdout, log.DebugLevel, false) + emuKMS := kms.NewEKMS(myName, uuid.New(), os.Stdout, log.TraceLevel, false) // Fire up Quantum LinK - myQL := emuKMS.AddQuantumElement(myUDPAddr, true, os.Stdout, log.ErrorLevel, false) + myQL := emuKMS.AddQuantumElement(myUDPAddr, generatedKeys, os.Stdout, log.TraceLevel, false) udpQL2Addr, err := net.ResolveUDPAddr("udp", peerUDPAddr) if err != nil { diff --git a/internal/quantumlayer/quantumlayer-emu-prng.go b/internal/quantumlayer/quantumlayer-emu-prng.go index 8bb321a8f3b2dca4250dd080541b758888aaca53..50ca518ca167f4eeba93f9b8dd1c3949f2f6035d 100644 --- a/internal/quantumlayer/quantumlayer-emu-prng.go +++ b/internal/quantumlayer/quantumlayer-emu-prng.go @@ -35,7 +35,6 @@ type QuantumlayerEmuPRNG struct { poweron bool // set to yes if operation, i.e., generating keys generateKeys bool // set to yes, if this qle should generate random number. incomingRandNums chan QuantumPayloadElement - outgoingRandNums chan QuantumPayloadElement peerNumbers *NumberStore myNumbers *NumberStore localQLAddress string @@ -77,7 +76,6 @@ func NewQuantumlayerEmuPRNG(logOutput io.Writer, logLevel logi.Level, logInJson poweron: false, generateKeys: false, incomingRandNums: make(chan QuantumPayloadElement), - outgoingRandNums: make(chan QuantumPayloadElement), peerNumbers: NewNumberStore(40000), myNumbers: NewNumberStore(40000), qlPeer: "", @@ -89,7 +87,6 @@ func (qlemuprng *QuantumlayerEmuPRNG) Configure(localQLAddress ...string) { // Start receiving numberstores go qlemuprng.peerNumbers.receiveNumbers(qlemuprng.incomingRandNums) - go qlemuprng.myNumbers.receiveNumbers(qlemuprng.outgoingRandNums) // Determine if a local UDP address should be used or not if len(localQLAddress) == 0 { @@ -227,7 +224,7 @@ func (qlemuprng *QuantumlayerEmuPRNG) AddPeer(addr *net.UDPAddr) { if err != nil { log.Fatalf("QuantumlayerEmuPRNG: WriteMsgUDPAddrPort failed: %s", err) } - qlemuprng.outgoingRandNums <- qpe + qlemuprng.incomingRandNums <- qpe } // TODO: This sleep timer has to replaced by something for clever. time.Sleep(5 * time.Second) @@ -238,7 +235,7 @@ func (qlemuprng *QuantumlayerEmuPRNG) AddPeer(addr *net.UDPAddr) { } func (qlemuprng *QuantumlayerEmuPRNG) RemovePeer() { - if qlemuprng.poweron == false { + if !qlemuprng.poweron { return } @@ -271,14 +268,10 @@ func (qlemuprng *QuantumlayerEmuPRNG) GenerateRandomNumbers() (randNums []byte) return b } -func (qlemuprng *QuantumlayerEmuPRNG) GetKeyBatchPeer() (QuantumLayerBulkKey, error) { +func (qlemuprng *QuantumlayerEmuPRNG) GetKeyBulkPeer() (QuantumLayerBulkKey, error) { return qlemuprng.peerNumbers.GetBulk() } -func (qlemuprng *QuantumlayerEmuPRNG) GetKeyBatchLocal() (QuantumLayerBulkKey, error) { - return qlemuprng.myNumbers.GetBulk() -} - func (qlemuprng *QuantumlayerEmuPRNG) GetStatus() (poweredOn bool) { return qlemuprng.poweron } diff --git a/internal/quantumlayer/quantumlayer-emu-prng_test.go b/internal/quantumlayer/quantumlayer-emu-prng_test.go index 42e01717bb5712c6f8569def24e060bb42cb3f36..d13131d28bc84538bcc86d5896686efac7f78f5c 100644 --- a/internal/quantumlayer/quantumlayer-emu-prng_test.go +++ b/internal/quantumlayer/quantumlayer-emu-prng_test.go @@ -47,30 +47,14 @@ func TestQuantumLayer(t *testing.T) { time.Sleep(5 * time.Second) for n := 0; n < 2; n++ { - resultQl1, err := ql1.GetKeyBatchLocal() + resultQl1, err := ql1.GetKeyBulkPeer() if err == nil { t.Logf("run %d, *ql1* keyid %d \t keylen %d", n, resultQl1.BulkKeyId, resultQl1.BulkKeyLength) } else { t.Fatalf("Couldn't read local ql1 batch with error %s", err) } - //resultQl2 := ql2.GetBatchPeer() - resultQl2, err := ql2.GetKeyBatchPeer() - if err == nil { - t.Logf("*ql2* keyid %d \t keylen %d", resultQl2.BulkKeyId, resultQl2.BulkKeyLength) - } else { - t.Fatalf("Couldn't read local ql2 batch with error %s", err) - } - - if resultQl1.BulkKeyId != resultQl2.BulkKeyId { - log.Fatalf("Mismatching BulkKeyIds ql1 (%d) != ql2 (%d)", resultQl1.BulkKeyId, resultQl2.BulkKeyId) - } - - if resultQl1.BulkKeyLength != resultQl2.BulkKeyLength { - log.Fatalf("Mismatching BulkKeyLengths ql1 (%d) != ql2 (%d)", resultQl1.BulkKeyLength, resultQl2.BulkKeyLength) - } - - // TODO: Calculate checksum of BulkKey + // TODO: Calculate checksum of BulkKey and double-check time.Sleep(5 * time.Second) diff --git a/internal/quantumlayer/quantumlayer.go b/internal/quantumlayer/quantumlayer.go index 87e2589cb24a304b63b961e80a12f76dc2c8b16c..feaf7994227b38dee09f5aacd77cf5246ce46f30 100644 --- a/internal/quantumlayer/quantumlayer.go +++ b/internal/quantumlayer/quantumlayer.go @@ -11,16 +11,15 @@ type QuantumLayerBulkKey struct { } type QuantumLayer interface { - Configure(...string) // configure the interface, e.g., used IP/Port config if emulated - PowerOn(enableKeyGeneration bool) // switch on the quantum layer element - PowerOff() // switch off the quantum layer element - GetStatus() (poweredOn bool) // returns true if quantum layer element is powered on - AddPeer() // Adds a Quantum Layer Peer to the peer list - RemovePeer() // Remmoves a Quantum Layer Peer to the peer list - GetLocalQLPort() // Returns the information about the local quantum layer IP and port - GetKeyBatchPeer() (QuantumLayerBulkKey, error) // retrieve the bulk key received from peer - GetKeyBatchLocal() (QuantumLayerBulkKey, error) // retrieve the bulk key generated locally - GenerateRandomNumbers() []byte // generate a number of random numbers + Configure(...string) // configure the interface, e.g., used IP/Port config if emulated + PowerOn(enableKeyGeneration bool) // switch on the quantum layer element + PowerOff() // switch off the quantum layer element + GetStatus() (poweredOn bool) // returns true if quantum layer element is powered on + AddPeer() // Adds a Quantum Layer Peer to the peer list + RemovePeer() // Remmoves a Quantum Layer Peer to the peer list + GetLocalQLPort() // Returns the information about the local quantum layer IP and port + GetKeyBulkPeer() (QuantumLayerBulkKey, error) // retrieve the bulk key received from peer + GenerateRandomNumbers() []byte // generate a number of random numbers } type NumberLayer interface {