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"
"log"
"math/big"
"net"
"sync"
"time"
)
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
incomingRandNums chan QuantumPayloadElement
outgoingRandNums chan QuantumPayloadElement
peerNumbers *NumberStore
myNumbers *NumberStore
udpSrvConn *net.UDPConn
qlPeer string
qlPeerCancel context.CancelFunc
qlLocalPort *net.UDPAddr
qlPeerMutex sync.Mutex
}
func NewQuantumlayerEmuPRNG() (newql *QuantumlayerEmuPRNG) {
return &QuantumlayerEmuPRNG{
poweron: false,
incomingRandNums: make(chan QuantumPayloadElement),
outgoingRandNums: 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) {
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 {
// 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() {
if qlemuprng.configured == false {
// nothing do here move on
log.Printf("Sorry, the quantum layer is not configured for action. You've missed Configure()")
return
}
qlemuprng.poweron = false
log.Println("QuantumlayerEmuPRNG is powering on...charging.")
// serve UDP incoming
go func() {
// Get UDP server part going...
log.Printf("localQLAddress is %s", qlemuprng.localQLAddress)
// This reads random numbers from other Quantum end
udpSrvPort, err := net.ResolveUDPAddr("udp", qlemuprng.localQLAddress)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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)
// Ready, set, go!
qlemuprng.poweron = true
log.Printf("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.Printf("QuantumlayerEmuPRNG: Could not read from UDP: %s", err)
} else {
log.Printf("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.Printf("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.Printf("QuantumlayerEmuPRNG: Peer %s NOT listed", addr)
}
}
}
}()
// Wait for listening UDP socket in the above go-routine to get ready
for qlemuprng.poweron != true {
}
log.Println("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) {
if qlemuprng.poweron == false {
return
}
//TODO/XXX check the incoming addr
// Add peer to the ....
qlemuprng.qlPeerMutex.Lock()
qlemuprng.qlPeer = addr.String()
qlemuprng.qlPeerMutex.Unlock()
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 == true {
// 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.Printf("json.Marshal error %s", err)
}
_, _, err = qlemuprng.udpSrvConn.WriteMsgUDPAddrPort(jsonPayload, nil, addr.AddrPort())
if err != nil {
log.Fatalf("WriteMsgUDPAddrPort failed: %s", err)
}
qlemuprng.outgoingRandNums <- qpe
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
time.Sleep(5 * time.Second)
}
}
}(ctx)
}
func (qlemuprng *QuantumlayerEmuPRNG) RemovePeer() {
if qlemuprng.poweron == false {
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) GetKeyBatchPeer() (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
}
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
271
272
273
274
275
276
277
278
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
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.Println("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()
}
}