Newer
Older
package quantumlayer
/*
*
* This packages "implements", actually it only emulates, the output of a
* quantum link, i.e., the transmitted random numbers from one quantum
* sender to a quantum receiver.
* This relies on crypto/rand to generate the random numbers that will be
* transmitted to the other end.
*
*/
import (
"context"
"crypto/rand"
"encoding/json"
"errors"
"math/big"
"net"
"sync"
"time"
"github.com/sirupsen/logrus"
logi "github.com/sirupsen/logrus"
)
type QuantumPayloadElement struct {
BulkKeyId int64 `json:"bulk-key-id"` // the unique ID of this bulk of keys
BulkKeyLength int `json:"bulk-key-length"` // the length, counted in bytes, of bulkKey
BulkKey *[]byte `json:"bulk-key"` // the bulk key
}
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
peerNumbers *NumberStore
myNumbers *NumberStore
udpSrvConn *net.UDPConn
qlPeer string
qlPeerCancel context.CancelFunc
qlLocalPort *net.UDPAddr
qlPeerMutex sync.Mutex
}
// We use our own logrus instance, as we would like to have different log levels for different parts.
var log = logrus.New()
func NewQuantumlayerEmuPRNG(logOutput io.Writer, logLevel logi.Level, logInJson bool) (newql *QuantumlayerEmuPRNG) {
/*
* 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(&logi.TextFormatter{})
} else {
log.SetFormatter(&logi.JSONFormatter{})
}
// print code function if level is set to Trace
if logLevel == logi.TraceLevel {
log.SetReportCaller(true)
} else {
log.SetReportCaller(false)
}
// Return PRNG Quantum Layer
return &QuantumlayerEmuPRNG{
poweron: false,
incomingRandNums: make(chan QuantumPayloadElement),
peerNumbers: NewNumberStore(40000),
myNumbers: NewNumberStore(40000),
qlPeer: "",
}
}
// Configure the quantum emulation, but do not start if yet
func (qlemuprng *QuantumlayerEmuPRNG) Configure(localQLAddress ...string) {
// Start receiving numberstores
go qlemuprng.peerNumbers.receiveNumbers(qlemuprng.incomingRandNums)
// Determine if a local UDP address should be used or not
if len(localQLAddress) == 0 {
// No input
qlemuprng.localQLAddress = ":0"
} else {
qlemuprng.localQLAddress = localQLAddress[0]
}
qlemuprng.configured = true
}
// Power on the quantum layer, i.e., open up the communication ports for the
// other quantum module
func (qlemuprng *QuantumlayerEmuPRNG) PowerOn(enableKeyGeneration bool) {
if !qlemuprng.configured {
log.Errorf("QuantumlayerEmuPRNG: Sorry, the quantum layer is not configured for action. You've missed Configure()")
log.Infof("QuantumlayerEmuPRNG: is powering on...charging.")
if enableKeyGeneration {
log.Infof("QuantumlayerEmuPRNG: will GENERATE random keys")
}
qlemuprng.generateKeys = enableKeyGeneration
// serve UDP incoming
if qlemuprng.udpSrvConn == nil {
go func() {
// Get UDP server part going...
log.Debugf("QuantumlayerEmuPRNG: localQLAddress is %s", qlemuprng.localQLAddress)
// This reads random numbers from other Quantum end
udpSrvPort, err := net.ResolveUDPAddr("udp", qlemuprng.localQLAddress)
if err != nil {
log.Fatalf("QuantumlayerEmuPRNG: UDP failure: %s", err)
return
}
qlemuprng.udpSrvConn, err = net.ListenUDP("udp", udpSrvPort)
if err != nil {
log.Fatalf("QuantumlayerEmuPRNG: UDP failure: %s", err)
return
}
defer qlemuprng.udpSrvConn.Close()
// Retrieve local UDP address and store it for further actions.
qlemuprng.qlLocalPort = qlemuprng.udpSrvConn.LocalAddr().(*net.UDPAddr)
log.Infof("QuantumlayerEmuPRNG: started server, waiting for incoming rands on port %s \n", qlemuprng.udpSrvConn.LocalAddr().(*net.UDPAddr).String())
inBuffer := make([]byte, 1500)
for {
// Buffer for reading from "Quantum link"
n, addr, err := qlemuprng.udpSrvConn.ReadFromUDP(inBuffer)
if err != nil {
log.Errorf("QuantumlayerEmuPRNG: Could not read from UDP: %s", err)
} else {
log.Debugf("QuantumlayerEmuPRNG: read %d bytes from %s\n", n, addr)
// Check if sender of datagram is qlPeer
// Warning this is not checking the validity of the sender, i.e., spoofing is possible
if addr.String() == qlemuprng.qlPeer {
log.Debugf("QuantumlayerEmuPRNG: Peer %s listed", addr)
//dumb the received data into the channel and carry on
// TODO/XXX: no vetting for anything
// Unmarshall out of JSON
var inQBuffer QuantumPayloadElement
unmarshallErr := json.Unmarshal(inBuffer[0:n], &inQBuffer)
if unmarshallErr == nil {
qlemuprng.incomingRandNums <- inQBuffer
}
} else {
log.Infof("QuantumlayerEmuPRNG: Peer %s NOT listed", addr)
}
}
// Wait for listening UDP socket in the above go-routine to get ready
// Ready, set, go!
qlemuprng.poweron = true
log.Infof("QuantumlayerEmuPRNG: is charged and powered on.")
}
// Power off the quantum layer, i.e., close the communication ports for the
// other quantum module
func (qlemuprng *QuantumlayerEmuPRNG) PowerOff() {
qlemuprng.poweron = false
log.Println("QuantumlayerEmuPRNG: is powered off...discharging.")
}
func (qlemuprng *QuantumlayerEmuPRNG) AddPeer(addr *net.UDPAddr) {
return
}
//TODO/XXX check the incoming addr
// Add peer to the ....
qlemuprng.qlPeerMutex.Lock()
qlemuprng.qlPeer = addr.String()
qlemuprng.qlPeerMutex.Unlock()
// generate only keys if requested to do so.
if qlemuprng.generateKeys {
ctx, cancel := context.WithCancel(context.Background())
qlemuprng.qlPeerCancel = cancel
// Start the generation and shipping of random numbers
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
if qlemuprng.poweron {
// retrieve a new back of random numbers
newNumberBatch := qlemuprng.GenerateRandomNumbers()
// TODO: Replace this by some generic encapsulation reader and not just JSON
//Get JSON for transmission ready
qpe := QuantumPayloadElement{time.Now().UnixNano(), len(newNumberBatch), &newNumberBatch}
// XXX/TODO: error must be handled
jsonPayload, err := json.Marshal(qpe)
if err != nil {
log.Errorf("QuantumlayerEmuPRNG: json.Marshal error %s", err)
}
_, _, err = qlemuprng.udpSrvConn.WriteMsgUDP(jsonPayload, nil, addr)
if err != nil {
log.Fatalf("QuantumlayerEmuPRNG: WriteMsgUDPAddrPort failed: %s", err)
}
// TODO: This sleep timer has to replaced by something for clever.
time.Sleep(5 * time.Second)
}
}
}
func (qlemuprng *QuantumlayerEmuPRNG) RemovePeer() {
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
return
}
// Stop the generation and shipping of random numbers
qlemuprng.qlPeerCancel()
// delete peer
qlemuprng.qlPeerMutex.Lock()
qlemuprng.qlPeer = ""
qlemuprng.qlPeerMutex.Unlock()
}
func (qlemuprng *QuantumlayerEmuPRNG) GetLocalQLPort() (myAddr net.UDPAddr) {
return *qlemuprng.qlLocalPort
}
func (qlemuprng *QuantumlayerEmuPRNG) GenerateRandomNumbers() (randNums []byte) {
numRands, randError := rand.Int(rand.Reader, big.NewInt(1000))
if randError != nil {
log.Fatalf("QuantumlayerEmuPRNG: %s", randError)
return
}
b := make([]byte, numRands.Uint64())
_, randError = rand.Read(b)
if randError != nil {
log.Fatalf("QuantumlayerEmuPRNG: %s", randError)
return
}
return b
}
func (qlemuprng *QuantumlayerEmuPRNG) GetKeyBulkPeer() (QuantumLayerBulkKey, error) {
return qlemuprng.peerNumbers.GetBulk()
}
func (qlemuprng *QuantumlayerEmuPRNG) GetStatus() (poweredOn bool) {
return qlemuprng.poweron
}
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
type NumberStore struct {
mu sync.Mutex
maxBytes int
storage []byte
bulkKeyStorage []QuantumLayerBulkKey
topOfStorage int
}
// Generates a new store with given maximum number of bytes
func NewNumberStore(maxBytes int) (newNS *NumberStore) {
return &NumberStore{
maxBytes: maxBytes,
storage: make([]byte, maxBytes),
topOfStorage: 0,
}
}
func (store *NumberStore) GetBulk() (bulk QuantumLayerBulkKey, err error) {
store.mu.Lock()
defer store.mu.Unlock()
for nextID := range store.bulkKeyStorage {
next := store.bulkKeyStorage[nextID]
// Preprare to return
a := QuantumLayerBulkKey{
BulkKeyId: next.BulkKeyId,
BulkKeyLength: next.BulkKeyLength,
BulkKey: nil,
}
bulkKey := make([]byte, next.BulkKeyLength)
copy(bulkKey, *next.BulkKey)
a.BulkKey = &bulkKey
// Delete from key store
store.bulkKeyStorage = store.bulkKeyStorage[1:]
// and return
return a, nil
}
returnErr := errors.New("no bulk key to retrieve")
b := QuantumLayerBulkKey{}
return b, returnErr
}
func (store *NumberStore) GetBatch() (batch []byte) {
store.mu.Lock()
defer store.mu.Unlock()
if store.topOfStorage != 0 {
log.Debugf("QuantumlayerEmuPRNG: Have Storage in my belly")
}
// prepare to return full batch of numbers
batchReturn := make([]byte, store.topOfStorage)
copy(batchReturn, store.storage)
store.topOfStorage = 0
return batchReturn
}
func (store *NumberStore) receiveNumbers(incoming chan QuantumPayloadElement) {
for {
receivedNumbers := <-incoming
// add received to our buffer, if buffer still last
store.mu.Lock()
mem := QuantumLayerBulkKey{
BulkKeyId: receivedNumbers.BulkKeyId,
BulkKeyLength: receivedNumbers.BulkKeyLength,
BulkKey: receivedNumbers.BulkKey,
}
//store.bulkKeyStorage[receivedNumbers.BulkKeyId] = mem
store.bulkKeyStorage = append(store.bulkKeyStorage, mem)
store.mu.Unlock()
}
}