· 6 years ago · Dec 28, 2019, 11:48 AM
1--[[ WiRe Server ]]--
2--[[ by Dog ]]--
3--[[ aka HydrantHunter ]]--
4--[[ Wi = Wireless ]]--
5--[[ Re = Redstone ]]--
6--[[ pastebin hqpRw4Jy ]]--
7local WiReSver = "2.0.05"
8--[[
9Tested with/requires:
10 - Minecraft 1.6.4+
11 - ComputerCraft 1.63+
12 - A Computer (standard or advanced) with a modem and one (1) advanced monitor array
13 - One (1) or more WiRe Clients running on one computer (standard or advanced) each with a modem
14
15Special thanks to: SquidDev (AES encryption/decryption)
16 Alex Kloss (base64 encoder/decoder)
17]]--
18--# CONFIGURATION
19--# Default Settings
20local termX, termY = term.getSize()
21local thisCC = tostring(os.getComputerID())
22local config = "/data/WiReServerCfg"
23local ccSettings = {
24 name = "WiReServer"; --# this server's name
25 note = "short note"; --# short note/description
26 color = "Silver"; --# network group
27 getGPSFix = true; --# get a GPS fix on startup
28 newColors = true; --# using new color names
29}
30local tArgs, modemSides, allClients, quietClients, loc = { ... }, { }, { }, { }, { }
31local pageNum, numPages, modemCount = 1, 1, 0
32local ccSuccess, kernelState, ccUpdate, help = false, false, false, false
33local network, client, thisCommand, pollTimer, mon, monX, monY, monSide, monControls, termScreenStatic, updateScreens, netReceive
34--# Terminal Colors
35local white = colors.white
36local black = colors.black
37local silver = colors.lightGray
38local gray = colors.gray
39local brown = colors.brown
40local yellow = colors.yellow
41local orange = colors.orange
42local red = colors.red
43local magenta = colors.magenta
44local purple = colors.purple
45local blue = colors.blue
46local sky = colors.lightBlue
47local cyan = colors.cyan
48local lime = colors.lime
49local green = colors.green
50if not term.isColor() then
51 silver = colors.white
52 gray = colors.black
53 brown = colors.white
54 yellow = colors.white
55 orange = colors.white
56 red = colors.white
57 magenta = colors.white
58 purple = colors.white
59 green = colors.white
60 blue = colors.black
61 sky = colors.white
62 cyan = colors.white
63 lime = colors.white
64 green = colors.white
65end
66--# Monitor colors
67local mwhite = colors.white
68local mblack = colors.black
69local msilver = colors.lightGray
70local mgray = colors.gray
71local mbrown = colors.brown
72local myellow = colors.yellow
73local morange = colors.orange
74local mred = colors.red
75local mmagenta = colors.magenta
76local mpurple = colors.purple
77local mblue = colors.blue
78local msky = colors.lightBlue
79local mcyan = colors.cyan
80local mlime = colors.lime
81local mgreen = colors.green
82--# Color tables
83local colorBurst = {
84 Purple = { purple, mpurple, mwhite };
85 Magenta = { magenta, mmagenta, mwhite };
86 Blue = { term.isColor() and blue or white, mblue, mwhite };
87 Sky = { sky, msky, mblack };
88 Cyan = { cyan, mcyan, mblack };
89 Green = { green, mgreen, mwhite };
90 Lime = { lime, mlime, mblack };
91 Red = { red, mred, mwhite };
92 Orange = { orange, morange, mblack };
93 Yellow = { yellow, myellow, mblack };
94 Brown = { brown, mbrown, mwhite };
95 Silver = { silver, msilver, mblack };
96 Gray = { term.isColor() and gray or white, mgray, mwhite };
97 White = { white, mwhite, mblack };
98 Black = { white, mblack, mwhite };
99}
100local validStates = {
101 OPEN = { morange, orange, "CLOSED" };
102 CLOSED = { mgreen, green, "OPEN" };
103 ON = { mgreen, green, "OFF" };
104 OFF = { morange, orange, "ON" };
105 LOCKED = { mred, red, "" };
106 UNLOCK = { mgreen, green, "" };
107 OFFLINE = { mred, red, "" };
108 WiReQRY = { myellow, yellow, "" };
109 init = { myellow, yellow, "" };
110 Noise = { mred, red, "" };
111}
112--# END CONFIGURATION
113
114-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
115-- licensed under the terms of the LGPL2
116-- http://lua-users.org/wiki/BaseSixtyFour
117-- character table string
118local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
119-- encoding
120function encode(data)
121 return ((data:gsub('.', function(x)
122 local r,b='',x:byte()
123 for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
124 return r;
125 end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
126 if (#x < 6) then return '' end
127 local c=0
128 for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
129 return b:sub(c+1,c+1)
130 end)..({ '', '==', '=' })[#data%3+1])
131end
132-- decoding
133function decode(data)
134 data = string.gsub(data, '[^'..b..'=]', '')
135 return (data:gsub('.', function(x)
136 if (x == '=') then return '' end
137 local r,f='',(b:find(x)-1)
138 for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
139 return r;
140 end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
141 if (#x ~= 8) then return '' end
142 local c=0
143 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
144 return string.char(c)
145 end))
146end
147
148-- AES Lua implementation by SquidDev
149-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
150local encrypt, decrypt
151do
152 local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
153 local bit=_W(function(_ENV, ...)
154 --[[
155 This bit API is designed to cope with unsigned integers instead of normal integers
156 To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
157 These are written in long form because no constant folding.
158 ]]
159 local floor = math.floor
160 local lshift, rshift
161
162 rshift = function(a,disp)
163 return floor(a % 4294967296 / 2^disp)
164 end
165
166 lshift = function(a,disp)
167 return (a * 2^disp) % 4294967296
168 end
169
170 return {
171 -- bit operations
172 bnot = bit32 and bit32.bnot or bit.bnot,
173 band = bit32 and bit32.band or bit.band,
174 bor = bit32 and bit32.bor or bit.bor,
175 bxor = bit32 and bit32.bxor or bit.bxor,
176 rshift = rshift,
177 lshift = lshift,
178 }
179 end)
180
181 local gf=_W(function(_ENV, ...)
182 -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
183 local bxor = bit32 and bit32.bxor or bit.bxor
184 local lshift = bit.lshift
185 -- private data of gf
186 local n = 0x100
187 local ord = 0xff
188 local irrPolynom = 0x11b
189 local exp = {}
190 local log = {}
191 --
192 -- add two polynoms (its simply xor)
193 --
194 local function add(operand1, operand2)
195 return bxor(operand1,operand2)
196 end
197 --
198 -- subtract two polynoms (same as addition)
199 --
200 local function sub(operand1, operand2)
201 return bxor(operand1,operand2)
202 end
203 --
204 -- inverts element
205 -- a^(-1) = g^(order - log(a))
206 --
207 local function invert(operand)
208 -- special case for 1
209 if (operand == 1) then
210 return 1
211 end
212 -- normal invert
213 local exponent = ord - log[operand]
214 return exp[exponent]
215 end
216 --
217 -- multiply two elements using a logarithm table
218 -- a*b = g^(log(a)+log(b))
219 --
220 local function mul(operand1, operand2)
221 if (operand1 == 0 or operand2 == 0) then
222 return 0
223 end
224 local exponent = log[operand1] + log[operand2]
225 if (exponent >= ord) then
226 exponent = exponent - ord
227 end
228 return exp[exponent]
229 end
230 --
231 -- divide two elements
232 -- a/b = g^(log(a)-log(b))
233 --
234 local function div(operand1, operand2)
235 if (operand1 == 0) then
236 return 0
237 end
238 -- TODO: exception if operand2 == 0
239 local exponent = log[operand1] - log[operand2]
240 if (exponent < 0) then
241 exponent = exponent + ord
242 end
243 return exp[exponent]
244 end
245 --
246 -- print logarithmic table
247 --
248 local function printLog()
249 for i = 1, n do
250 print("log(", i-1, ")=", log[i-1])
251 end
252 end
253 --
254 -- print exponentiation table
255 --
256 local function printExp()
257 for i = 1, n do
258 print("exp(", i-1, ")=", exp[i-1])
259 end
260 end
261 --
262 -- calculate logarithmic and exponentiation table
263 --
264 local function initMulTable()
265 local a = 1
266 for i = 0,ord-1 do
267 exp[i] = a
268 log[a] = i
269 -- multiply with generator x+1 -> left shift + 1
270 a = bxor(lshift(a, 1), a)
271 -- if a gets larger than order, reduce modulo irreducible polynom
272 if a > ord then
273 a = sub(a, irrPolynom)
274 end
275 end
276 end
277
278 initMulTable()
279
280 return {
281 add = add,
282 sub = sub,
283 invert = invert,
284 mul = mul,
285 div = div,
286 printLog = printLog,
287 printExp = printExp,
288 }
289 end)
290
291 util=_W(function(_ENV, ...)
292 -- Cache some bit operators
293 local bxor = bit.bxor
294 local rshift = bit.rshift
295 local band = bit.band
296 local lshift = bit.lshift
297 local sleepCheckIn
298 --
299 -- calculate the parity of one byte
300 --
301 local function byteParity(byte)
302 byte = bxor(byte, rshift(byte, 4))
303 byte = bxor(byte, rshift(byte, 2))
304 byte = bxor(byte, rshift(byte, 1))
305 return band(byte, 1)
306 end
307 --
308 -- get byte at position index
309 --
310 local function getByte(number, index)
311 return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
312 end
313 --
314 -- put number into int at position index
315 --
316 local function putByte(number, index)
317 return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
318 end
319 --
320 -- convert byte array to int array
321 --
322 local function bytesToInts(bytes, start, n)
323 local ints = {}
324 for i = 0, n - 1 do
325 ints[i + 1] =
326 putByte(bytes[start + (i*4)], 3) +
327 putByte(bytes[start + (i*4) + 1], 2) +
328 putByte(bytes[start + (i*4) + 2], 1) +
329 putByte(bytes[start + (i*4) + 3], 0)
330 if n % 10000 == 0 then sleepCheckIn() end
331 end
332 return ints
333 end
334 --
335 -- convert int array to byte array
336 --
337 local function intsToBytes(ints, output, outputOffset, n)
338 n = n or #ints
339 for i = 0, n - 1 do
340 for j = 0,3 do
341 output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
342 end
343 if n % 10000 == 0 then sleepCheckIn() end
344 end
345 return output
346 end
347 --
348 -- convert bytes to hexString
349 --
350 local function bytesToHex(bytes)
351 local hexBytes = ""
352 for i,byte in ipairs(bytes) do
353 hexBytes = hexBytes .. string.format("%02x ", byte)
354 end
355 return hexBytes
356 end
357
358 local function hexToBytes(bytes)
359 local out = {}
360 for i = 1, #bytes, 2 do
361 out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
362 end
363 return out
364 end
365 --
366 -- convert data to hex string
367 --
368 local function toHexString(data)
369 local type = type(data)
370 if (type == "number") then
371 return string.format("%08x",data)
372 elseif (type == "table") then
373 return bytesToHex(data)
374 elseif (type == "string") then
375 local bytes = {string.byte(data, 1, #data)}
376 return bytesToHex(bytes)
377 else
378 return data
379 end
380 end
381
382 local function padByteString(data)
383 local dataLength = #data
384 local random1 = math.random(0,255)
385 local random2 = math.random(0,255)
386 local prefix = string.char(random1,
387 random2,
388 random1,
389 random2,
390 getByte(dataLength, 3),
391 getByte(dataLength, 2),
392 getByte(dataLength, 1),
393 getByte(dataLength, 0)
394 )
395 data = prefix .. data
396 local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
397 for i=1,paddingLength do
398 padding = padding .. string.char(math.random(0,255))
399 end
400 return data .. padding
401 end
402
403 local function properlyDecrypted(data)
404 local random = {string.byte(data,1,4)}
405 if (random[1] == random[3] and random[2] == random[4]) then
406 return true
407 end
408 return false
409 end
410
411 local function unpadByteString(data)
412 if (not properlyDecrypted(data)) then
413 return nil
414 end
415 local dataLength = putByte(string.byte(data,5), 3)
416 + putByte(string.byte(data,6), 2)
417 + putByte(string.byte(data,7), 1)
418 + putByte(string.byte(data,8), 0)
419 return string.sub(data,9,8+dataLength)
420 end
421
422 local function xorIV(data, iv)
423 for i = 1,16 do
424 data[i] = bxor(data[i], iv[i])
425 end
426 end
427
428 local function increment(data)
429 local i = 16
430 while true do
431 local value = data[i] + 1
432 if value >= 256 then
433 data[i] = value - 256
434 i = (i - 2) % 16 + 1
435 else
436 data[i] = value
437 break
438 end
439 end
440 end
441
442 -- Called every encryption cycle
443 local push, pull, time = os.queueEvent, coroutine.yield, os.time
444 local oldTime = time()
445 local function sleepCheckIn()
446 local newTime = time()
447 if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
448 oldTime = newTime
449 push("sleep")
450 pull("sleep")
451 end
452 end
453
454 local function getRandomData(bytes)
455 local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
456 local result = {}
457 for i=1,bytes do
458 insert(result, random(0,255))
459 if i % 10240 == 0 then sleep() end
460 end
461 return result
462 end
463
464 local function getRandomString(bytes)
465 local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
466 local result = {}
467 for i=1,bytes do
468 insert(result, char(random(0,255)))
469 if i % 10240 == 0 then sleep() end
470 end
471 return table.concat(result)
472 end
473
474 return {
475 byteParity = byteParity,
476 getByte = getByte,
477 putByte = putByte,
478 bytesToInts = bytesToInts,
479 intsToBytes = intsToBytes,
480 bytesToHex = bytesToHex,
481 hexToBytes = hexToBytes,
482 toHexString = toHexString,
483 padByteString = padByteString,
484 properlyDecrypted = properlyDecrypted,
485 unpadByteString = unpadByteString,
486 xorIV = xorIV,
487 increment = increment,
488 sleepCheckIn = sleepCheckIn,
489 getRandomData = getRandomData,
490 getRandomString = getRandomString,
491 }
492 end)
493
494 aes=_W(function(_ENV, ...)
495 -- Implementation of AES with nearly pure lua
496 -- AES with lua is slow, really slow :-)
497 local putByte = util.putByte
498 local getByte = util.getByte
499 -- some constants
500 local ROUNDS = 'rounds'
501 local KEY_TYPE = "type"
502 local ENCRYPTION_KEY=1
503 local DECRYPTION_KEY=2
504 -- aes SBOX
505 local SBox = {}
506 local iSBox = {}
507 -- aes tables
508 local table0 = {}
509 local table1 = {}
510 local table2 = {}
511 local table3 = {}
512 local tableInv0 = {}
513 local tableInv1 = {}
514 local tableInv2 = {}
515 local tableInv3 = {}
516 -- round constants
517 local rCon = {
518 0x01000000,
519 0x02000000,
520 0x04000000,
521 0x08000000,
522 0x10000000,
523 0x20000000,
524 0x40000000,
525 0x80000000,
526 0x1b000000,
527 0x36000000,
528 0x6c000000,
529 0xd8000000,
530 0xab000000,
531 0x4d000000,
532 0x9a000000,
533 0x2f000000,
534 }
535 --
536 -- affine transformation for calculating the S-Box of AES
537 --
538 local function affinMap(byte)
539 mask = 0xf8
540 result = 0
541 for i = 1,8 do
542 result = bit.lshift(result,1)
543 parity = util.byteParity(bit.band(byte,mask))
544 result = result + parity
545 -- simulate roll
546 lastbit = bit.band(mask, 1)
547 mask = bit.band(bit.rshift(mask, 1),0xff)
548 mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
549 end
550 return bit.bxor(result, 0x63)
551 end
552 --
553 -- calculate S-Box and inverse S-Box of AES
554 -- apply affine transformation to inverse in finite field 2^8
555 --
556 local function calcSBox()
557 for i = 0, 255 do
558 inverse = i ~= 0 and gf.invert(i) or i
559 mapped = affinMap(inverse)
560 SBox[i] = mapped
561 iSBox[mapped] = i
562 end
563 end
564 --
565 -- Calculate round tables
566 -- round tables are used to calculate shiftRow, MixColumn and SubBytes
567 -- with 4 table lookups and 4 xor operations.
568 --
569 local function calcRoundTables()
570 for x = 0,255 do
571 byte = SBox[x]
572 table0[x] = putByte(gf.mul(0x03, byte), 0)
573 + putByte( byte , 1)
574 + putByte( byte , 2)
575 + putByte(gf.mul(0x02, byte), 3)
576 table1[x] = putByte( byte , 0)
577 + putByte( byte , 1)
578 + putByte(gf.mul(0x02, byte), 2)
579 + putByte(gf.mul(0x03, byte), 3)
580 table2[x] = putByte( byte , 0)
581 + putByte(gf.mul(0x02, byte), 1)
582 + putByte(gf.mul(0x03, byte), 2)
583 + putByte( byte , 3)
584 table3[x] = putByte(gf.mul(0x02, byte), 0)
585 + putByte(gf.mul(0x03, byte), 1)
586 + putByte( byte , 2)
587 + putByte( byte , 3)
588 end
589 end
590 --
591 -- Calculate inverse round tables
592 -- does the inverse of the normal roundtables for the equivalent
593 -- decryption algorithm.
594 --
595 local function calcInvRoundTables()
596 for x = 0,255 do
597 byte = iSBox[x]
598 tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
599 + putByte(gf.mul(0x0d, byte), 1)
600 + putByte(gf.mul(0x09, byte), 2)
601 + putByte(gf.mul(0x0e, byte), 3)
602 tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
603 + putByte(gf.mul(0x09, byte), 1)
604 + putByte(gf.mul(0x0e, byte), 2)
605 + putByte(gf.mul(0x0b, byte), 3)
606 tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
607 + putByte(gf.mul(0x0e, byte), 1)
608 + putByte(gf.mul(0x0b, byte), 2)
609 + putByte(gf.mul(0x0d, byte), 3)
610 tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
611 + putByte(gf.mul(0x0b, byte), 1)
612 + putByte(gf.mul(0x0d, byte), 2)
613 + putByte(gf.mul(0x09, byte), 3)
614 end
615 end
616 --
617 -- rotate word: 0xaabbccdd gets 0xbbccddaa
618 -- used for key schedule
619 --
620 local function rotWord(word)
621 local tmp = bit.band(word,0xff000000)
622 return (bit.lshift(word,8) + bit.rshift(tmp,24))
623 end
624 --
625 -- replace all bytes in a word with the SBox.
626 -- used for key schedule
627 --
628 local function subWord(word)
629 return putByte(SBox[getByte(word,0)],0)
630 + putByte(SBox[getByte(word,1)],1)
631 + putByte(SBox[getByte(word,2)],2)
632 + putByte(SBox[getByte(word,3)],3)
633 end
634 --
635 -- generate key schedule for aes encryption
636 --
637 -- returns table with all round keys and
638 -- the necessary number of rounds saved in [ROUNDS]
639 --
640 local function expandEncryptionKey(key)
641 local keySchedule = {}
642 local keyWords = math.floor(#key / 4)
643 if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
644 error("Invalid key size: " .. tostring(keyWords))
645 return nil
646 end
647 keySchedule[ROUNDS] = keyWords + 6
648 keySchedule[KEY_TYPE] = ENCRYPTION_KEY
649 for i = 0,keyWords - 1 do
650 keySchedule[i] = putByte(key[i*4+1], 3)
651 + putByte(key[i*4+2], 2)
652 + putByte(key[i*4+3], 1)
653 + putByte(key[i*4+4], 0)
654 end
655 for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
656 local tmp = keySchedule[i-1]
657 if ( i % keyWords == 0) then
658 tmp = rotWord(tmp)
659 tmp = subWord(tmp)
660 local index = math.floor(i/keyWords)
661 tmp = bit.bxor(tmp,rCon[index])
662 elseif (keyWords > 6 and i % keyWords == 4) then
663 tmp = subWord(tmp)
664 end
665 keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
666 end
667 return keySchedule
668 end
669 --
670 -- Inverse mix column
671 -- used for key schedule of decryption key
672 --
673 local function invMixColumnOld(word)
674 local b0 = getByte(word,3)
675 local b1 = getByte(word,2)
676 local b2 = getByte(word,1)
677 local b3 = getByte(word,0)
678 return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
679 gf.mul(0x0d, b2)),
680 gf.mul(0x09, b3)),
681 gf.mul(0x0e, b0)),3)
682 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
683 gf.mul(0x0d, b3)),
684 gf.mul(0x09, b0)),
685 gf.mul(0x0e, b1)),2)
686 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
687 gf.mul(0x0d, b0)),
688 gf.mul(0x09, b1)),
689 gf.mul(0x0e, b2)),1)
690 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
691 gf.mul(0x0d, b1)),
692 gf.mul(0x09, b2)),
693 gf.mul(0x0e, b3)),0)
694 end
695 --
696 -- Optimized inverse mix column
697 -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
698 -- TODO: make it work
699 --
700 local function invMixColumn(word)
701 local b0 = getByte(word,3)
702 local b1 = getByte(word,2)
703 local b2 = getByte(word,1)
704 local b3 = getByte(word,0)
705 local t = bit.bxor(b3,b2)
706 local u = bit.bxor(b1,b0)
707 local v = bit.bxor(t,u)
708 v = bit.bxor(v,gf.mul(0x08,v))
709 w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
710 v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
711 return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
712 + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1)
713 + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
714 + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3)
715 end
716 --
717 -- generate key schedule for aes decryption
718 --
719 -- uses key schedule for aes encryption and transforms each
720 -- key by inverse mix column.
721 --
722 local function expandDecryptionKey(key)
723 local keySchedule = expandEncryptionKey(key)
724 if (keySchedule == nil) then
725 return nil
726 end
727 keySchedule[KEY_TYPE] = DECRYPTION_KEY
728 for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
729 keySchedule[i] = invMixColumnOld(keySchedule[i])
730 end
731 return keySchedule
732 end
733 --
734 -- xor round key to state
735 --
736 local function addRoundKey(state, key, round)
737 for i = 0, 3 do
738 state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
739 end
740 end
741 --
742 -- do encryption round (ShiftRow, SubBytes, MixColumn together)
743 --
744 local function doRound(origState, dstState)
745 dstState[1] = bit.bxor(bit.bxor(bit.bxor(
746 table0[getByte(origState[1],3)],
747 table1[getByte(origState[2],2)]),
748 table2[getByte(origState[3],1)]),
749 table3[getByte(origState[4],0)])
750 dstState[2] = bit.bxor(bit.bxor(bit.bxor(
751 table0[getByte(origState[2],3)],
752 table1[getByte(origState[3],2)]),
753 table2[getByte(origState[4],1)]),
754 table3[getByte(origState[1],0)])
755 dstState[3] = bit.bxor(bit.bxor(bit.bxor(
756 table0[getByte(origState[3],3)],
757 table1[getByte(origState[4],2)]),
758 table2[getByte(origState[1],1)]),
759 table3[getByte(origState[2],0)])
760 dstState[4] = bit.bxor(bit.bxor(bit.bxor(
761 table0[getByte(origState[4],3)],
762 table1[getByte(origState[1],2)]),
763 table2[getByte(origState[2],1)]),
764 table3[getByte(origState[3],0)])
765 end
766 --
767 -- do last encryption round (ShiftRow and SubBytes)
768 --
769 local function doLastRound(origState, dstState)
770 dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
771 + putByte(SBox[getByte(origState[2],2)], 2)
772 + putByte(SBox[getByte(origState[3],1)], 1)
773 + putByte(SBox[getByte(origState[4],0)], 0)
774 dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
775 + putByte(SBox[getByte(origState[3],2)], 2)
776 + putByte(SBox[getByte(origState[4],1)], 1)
777 + putByte(SBox[getByte(origState[1],0)], 0)
778 dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
779 + putByte(SBox[getByte(origState[4],2)], 2)
780 + putByte(SBox[getByte(origState[1],1)], 1)
781 + putByte(SBox[getByte(origState[2],0)], 0)
782 dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
783 + putByte(SBox[getByte(origState[1],2)], 2)
784 + putByte(SBox[getByte(origState[2],1)], 1)
785 + putByte(SBox[getByte(origState[3],0)], 0)
786 end
787 --
788 -- do decryption round
789 --
790 local function doInvRound(origState, dstState)
791 dstState[1] = bit.bxor(bit.bxor(bit.bxor(
792 tableInv0[getByte(origState[1],3)],
793 tableInv1[getByte(origState[4],2)]),
794 tableInv2[getByte(origState[3],1)]),
795 tableInv3[getByte(origState[2],0)])
796 dstState[2] = bit.bxor(bit.bxor(bit.bxor(
797 tableInv0[getByte(origState[2],3)],
798 tableInv1[getByte(origState[1],2)]),
799 tableInv2[getByte(origState[4],1)]),
800 tableInv3[getByte(origState[3],0)])
801 dstState[3] = bit.bxor(bit.bxor(bit.bxor(
802 tableInv0[getByte(origState[3],3)],
803 tableInv1[getByte(origState[2],2)]),
804 tableInv2[getByte(origState[1],1)]),
805 tableInv3[getByte(origState[4],0)])
806 dstState[4] = bit.bxor(bit.bxor(bit.bxor(
807 tableInv0[getByte(origState[4],3)],
808 tableInv1[getByte(origState[3],2)]),
809 tableInv2[getByte(origState[2],1)]),
810 tableInv3[getByte(origState[1],0)])
811 end
812 --
813 -- do last decryption round
814 --
815 local function doInvLastRound(origState, dstState)
816 dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
817 + putByte(iSBox[getByte(origState[4],2)], 2)
818 + putByte(iSBox[getByte(origState[3],1)], 1)
819 + putByte(iSBox[getByte(origState[2],0)], 0)
820 dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
821 + putByte(iSBox[getByte(origState[1],2)], 2)
822 + putByte(iSBox[getByte(origState[4],1)], 1)
823 + putByte(iSBox[getByte(origState[3],0)], 0)
824 dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
825 + putByte(iSBox[getByte(origState[2],2)], 2)
826 + putByte(iSBox[getByte(origState[1],1)], 1)
827 + putByte(iSBox[getByte(origState[4],0)], 0)
828 dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
829 + putByte(iSBox[getByte(origState[3],2)], 2)
830 + putByte(iSBox[getByte(origState[2],1)], 1)
831 + putByte(iSBox[getByte(origState[1],0)], 0)
832 end
833 --
834 -- encrypts 16 Bytes
835 -- key encryption key schedule
836 -- input array with input data
837 -- inputOffset start index for input
838 -- output array for encrypted data
839 -- outputOffset start index for output
840 --
841 local function encrypt(key, input, inputOffset, output, outputOffset)
842 --default parameters
843 inputOffset = inputOffset or 1
844 output = output or {}
845 outputOffset = outputOffset or 1
846 local state = {}
847 local tmpState = {}
848 if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
849 error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
850 return
851 end
852 state = util.bytesToInts(input, inputOffset, 4)
853 addRoundKey(state, key, 0)
854 local round = 1
855 while (round < key[ROUNDS] - 1) do
856 -- do a double round to save temporary assignments
857 doRound(state, tmpState)
858 addRoundKey(tmpState, key, round)
859 round = round + 1
860 doRound(tmpState, state)
861 addRoundKey(state, key, round)
862 round = round + 1
863 end
864 doRound(state, tmpState)
865 addRoundKey(tmpState, key, round)
866 round = round +1
867 doLastRound(tmpState, state)
868 addRoundKey(state, key, round)
869 util.sleepCheckIn()
870 return util.intsToBytes(state, output, outputOffset)
871 end
872 --
873 -- decrypt 16 bytes
874 -- key decryption key schedule
875 -- input array with input data
876 -- inputOffset start index for input
877 -- output array for decrypted data
878 -- outputOffset start index for output
879 ---
880 local function decrypt(key, input, inputOffset, output, outputOffset)
881 -- default arguments
882 inputOffset = inputOffset or 1
883 output = output or {}
884 outputOffset = outputOffset or 1
885 local state = {}
886 local tmpState = {}
887 if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
888 error("No decryption key: " .. tostring(key[KEY_TYPE]))
889 return
890 end
891 state = util.bytesToInts(input, inputOffset, 4)
892 addRoundKey(state, key, key[ROUNDS])
893 local round = key[ROUNDS] - 1
894 while (round > 2) do
895 -- do a double round to save temporary assignments
896 doInvRound(state, tmpState)
897 addRoundKey(tmpState, key, round)
898 round = round - 1
899 doInvRound(tmpState, state)
900 addRoundKey(state, key, round)
901 round = round - 1
902 end
903 doInvRound(state, tmpState)
904 addRoundKey(tmpState, key, round)
905 round = round - 1
906 doInvLastRound(tmpState, state)
907 addRoundKey(state, key, round)
908 util.sleepCheckIn()
909 return util.intsToBytes(state, output, outputOffset)
910 end
911
912 -- calculate all tables when loading this file
913 calcSBox()
914 calcRoundTables()
915 calcInvRoundTables()
916
917 return {
918 ROUNDS = ROUNDS,
919 KEY_TYPE = KEY_TYPE,
920 ENCRYPTION_KEY = ENCRYPTION_KEY,
921 DECRYPTION_KEY = DECRYPTION_KEY,
922 expandEncryptionKey = expandEncryptionKey,
923 expandDecryptionKey = expandDecryptionKey,
924 encrypt = encrypt,
925 decrypt = decrypt,
926 }
927 end)
928
929 local buffer=_W(function(_ENV, ...)
930 local function new ()
931 return {}
932 end
933
934 local function addString (stack, s)
935 table.insert(stack, s)
936 end
937
938 local function toString (stack)
939 return table.concat(stack)
940 end
941
942 return {
943 new = new,
944 addString = addString,
945 toString = toString,
946 }
947 end)
948
949 ciphermode=_W(function(_ENV, ...)
950 local public = {}
951 --
952 -- Encrypt strings
953 -- key - byte array with key
954 -- string - string to encrypt
955 -- modefunction - function for cipher mode to use
956 --
957 local random, unpack = math.random, unpack or table.unpack
958 function public.encryptString(key, data, modeFunction, iv)
959 if iv then
960 local ivCopy = {}
961 for i = 1, 16 do ivCopy[i] = iv[i] end
962 iv = ivCopy
963 else
964 iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
965 end
966 local keySched = aes.expandEncryptionKey(key)
967 local encryptedData = buffer.new()
968 for i = 1, #data/16 do
969 local offset = (i-1)*16 + 1
970 local byteData = {string.byte(data,offset,offset +15)}
971 iv = modeFunction(keySched, byteData, iv)
972 buffer.addString(encryptedData, string.char(unpack(byteData)))
973 end
974 return buffer.toString(encryptedData)
975 end
976 --
977 -- the following 4 functions can be used as
978 -- modefunction for encryptString
979 --
980 -- Electronic code book mode encrypt function
981 function public.encryptECB(keySched, byteData, iv)
982 aes.encrypt(keySched, byteData, 1, byteData, 1)
983 end
984
985 -- Cipher block chaining mode encrypt function
986 function public.encryptCBC(keySched, byteData, iv)
987 util.xorIV(byteData, iv)
988 aes.encrypt(keySched, byteData, 1, byteData, 1)
989 return byteData
990 end
991
992 -- Output feedback mode encrypt function
993 function public.encryptOFB(keySched, byteData, iv)
994 aes.encrypt(keySched, iv, 1, iv, 1)
995 util.xorIV(byteData, iv)
996 return iv
997 end
998
999 -- Cipher feedback mode encrypt function
1000 function public.encryptCFB(keySched, byteData, iv)
1001 aes.encrypt(keySched, iv, 1, iv, 1)
1002 util.xorIV(byteData, iv)
1003 return byteData
1004 end
1005
1006 function public.encryptCTR(keySched, byteData, iv)
1007 local nextIV = {}
1008 for j = 1, 16 do nextIV[j] = iv[j] end
1009 aes.encrypt(keySched, iv, 1, iv, 1)
1010 util.xorIV(byteData, iv)
1011 util.increment(nextIV)
1012 return nextIV
1013 end
1014 --
1015 -- Decrypt strings
1016 -- key - byte array with key
1017 -- string - string to decrypt
1018 -- modefunction - function for cipher mode to use
1019 --
1020 function public.decryptString(key, data, modeFunction, iv)
1021 if iv then
1022 local ivCopy = {}
1023 for i = 1, 16 do ivCopy[i] = iv[i] end
1024 iv = ivCopy
1025 else
1026 iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1027 end
1028 local keySched
1029 if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
1030 keySched = aes.expandEncryptionKey(key)
1031 else
1032 keySched = aes.expandDecryptionKey(key)
1033 end
1034 local decryptedData = buffer.new()
1035 for i = 1, #data/16 do
1036 local offset = (i-1)*16 + 1
1037 local byteData = {string.byte(data,offset,offset +15)}
1038 iv = modeFunction(keySched, byteData, iv)
1039 buffer.addString(decryptedData, string.char(unpack(byteData)))
1040 end
1041 return buffer.toString(decryptedData)
1042 end
1043 --
1044 -- the following 4 functions can be used as
1045 -- modefunction for decryptString
1046 --
1047 -- Electronic code book mode decrypt function
1048 function public.decryptECB(keySched, byteData, iv)
1049 aes.decrypt(keySched, byteData, 1, byteData, 1)
1050 return iv
1051 end
1052
1053 -- Cipher block chaining mode decrypt function
1054 function public.decryptCBC(keySched, byteData, iv)
1055 local nextIV = {}
1056 for j = 1, 16 do nextIV[j] = byteData[j] end
1057 aes.decrypt(keySched, byteData, 1, byteData, 1)
1058 util.xorIV(byteData, iv)
1059 return nextIV
1060 end
1061
1062 -- Output feedback mode decrypt function
1063 function public.decryptOFB(keySched, byteData, iv)
1064 aes.encrypt(keySched, iv, 1, iv, 1)
1065 util.xorIV(byteData, iv)
1066 return iv
1067 end
1068
1069 -- Cipher feedback mode decrypt function
1070 function public.decryptCFB(keySched, byteData, iv)
1071 local nextIV = {}
1072 for j = 1, 16 do nextIV[j] = byteData[j] end
1073 aes.encrypt(keySched, iv, 1, iv, 1)
1074 util.xorIV(byteData, iv)
1075 return nextIV
1076 end
1077
1078 public.decryptCTR = public.encryptCTR
1079 return public
1080 end)
1081
1082 -- Simple API for encrypting strings.
1083 --
1084 AES128 = 16
1085 AES192 = 24
1086 AES256 = 32
1087 ECBMODE = 1
1088 CBCMODE = 2
1089 OFBMODE = 3
1090 CFBMODE = 4
1091 CTRMODE = 4
1092
1093 local function pwToKey(password, keyLength, iv)
1094 local padLength = keyLength
1095 if (keyLength == AES192) then
1096 padLength = 32
1097 end
1098 if (padLength > #password) then
1099 local postfix = ""
1100 for i = 1,padLength - #password do
1101 postfix = postfix .. string.char(0)
1102 end
1103 password = password .. postfix
1104 else
1105 password = string.sub(password, 1, padLength)
1106 end
1107 local pwBytes = {string.byte(password,1,#password)}
1108 password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
1109 password = string.sub(password, 1, keyLength)
1110 return {string.byte(password,1,#password)}
1111 end
1112 --
1113 -- Encrypts string data with password password.
1114 -- password - the encryption key is generated from this string
1115 -- data - string to encrypt (must not be too large)
1116 -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1117 -- mode - mode of encryption: ecb, cbc(default), ofb, cfb
1118 --
1119 -- mode and keyLength must be the same for encryption and decryption.
1120 --
1121 function encrypt(password, data, keyLength, mode, iv)
1122 assert(password ~= nil, "Empty password.")
1123 assert(data ~= nil, "Empty data.")
1124 local mode = mode or CBCMODE
1125 local keyLength = keyLength or AES128
1126 local key = pwToKey(password, keyLength, iv)
1127 local paddedData = util.padByteString(data)
1128 if mode == ECBMODE then
1129 return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
1130 elseif mode == CBCMODE then
1131 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
1132 elseif mode == OFBMODE then
1133 return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
1134 elseif mode == CFBMODE then
1135 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
1136 elseif mode == CTRMODE then
1137 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
1138 else
1139 error("Unknown mode", 2)
1140 end
1141 end
1142 --
1143 -- Decrypts string data with password password.
1144 -- password - the decryption key is generated from this string
1145 -- data - string to encrypt
1146 -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1147 -- mode - mode of decryption: ecb, cbc(default), ofb, cfb
1148 --
1149 -- mode and keyLength must be the same for encryption and decryption.
1150 --
1151 function decrypt(password, data, keyLength, mode, iv)
1152 local mode = mode or CBCMODE
1153 local keyLength = keyLength or AES128
1154 local key = pwToKey(password, keyLength, iv)
1155 local plain
1156 if mode == ECBMODE then
1157 plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
1158 elseif mode == CBCMODE then
1159 plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
1160 elseif mode == OFBMODE then
1161 plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
1162 elseif mode == CFBMODE then
1163 plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
1164 elseif mode == CTRMODE then
1165 plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
1166 else
1167 error("Unknown mode", 2)
1168 end
1169 result = util.unpadByteString(plain)
1170 if (result == nil) then
1171 return nil
1172 end
1173 return result
1174 end
1175end
1176
1177local function saveData()
1178 local srvConfig = fs.open(config, "w") or error("saveData(): Cannot open " .. config .. " for writing", 0)
1179 srvConfig.write(textutils.serialize(ccSettings))
1180 srvConfig.close()
1181end
1182
1183local function clearMon(bgColor)
1184 mon.setBackgroundColor(bgColor or mblack)
1185 mon.clear()
1186end
1187
1188local function clearMonButtons()
1189 local line = string.rep(" ", monX - 1)
1190 mon.setBackgroundColor(mblack)
1191 for i = 3, 8 do
1192 mon.setCursorPos(2, i)
1193 mon.write(line)
1194 end
1195end
1196
1197do
1198 local function splitName(name)
1199 if #name > 6 then
1200 if string.find(name, " ") then
1201 local firstPart, lastPart = name:sub(1, string.find(name, " ") - 1), name:sub(string.find(name, " ") + 1)
1202 if #firstPart <= 6 and #lastPart <= 6 then
1203 return firstPart, lastPart
1204 end
1205 end
1206 return name:sub(1, 6), name:sub(7)
1207 else
1208 return name, ""
1209 end
1210 end
1211
1212 local function powerSwitch(x, y, state)
1213 mon.setCursorPos(x, y)
1214 mon.setBackgroundColor((state == "OPEN" or state == "OFF") and mgray or mgreen)
1215 mon.write(" ") --# Switch Top (OFF/ON/OPEN/CLOSED)
1216 mon.setCursorPos(x, y + 1)
1217 mon.setBackgroundColor((state == "OPEN" or state == "OFF") and morange or mgray)
1218 mon.write(" ") --# Switch Bottom (OFF/ON/OPEN/CLOSED)
1219 end
1220
1221 monControls = function()
1222 if #allClients == 0 then
1223 mon.setTextColor(mred)
1224 mon.setCursorPos(4, 4)
1225 mon.write("No clients are")
1226 mon.setCursorPos(2, 5)
1227 mon.write("currently connected")
1228 else
1229 local firstName, lastName, xPos, devState, lockState
1230 for i = 1, #allClients do
1231 firstName, lastName = splitName(allClients[i].name)
1232 xPos, devState, lockState = (i * 8) - 6, allClients[i].deviceState, allClients[i].lockState
1233 mon.setBackgroundColor(mblack)
1234 mon.setTextColor(quietClients[i] and myellow or msky)
1235 mon.setCursorPos(xPos, 3)
1236 mon.write(firstName .. string.rep(" ", 6 - #firstName))
1237 mon.setCursorPos(xPos, 4)
1238 mon.write(lastName .. string.rep(" ", 6 - #lastName))
1239 mon.setCursorPos(xPos, 8)
1240 mon.setTextColor(lockState and mred or (validStates[devState][1] or myellow))
1241 mon.write(lockState and "LOCKED" or (devState .. string.rep(" ", 6 - #devState)))
1242 powerSwitch((i * 8) - 6, 5, devState)
1243 end
1244 end
1245 end
1246end
1247
1248local function clearTerm()
1249 term.setBackgroundColor(black)
1250 term.setTextColor(white)
1251 term.clear()
1252 term.setCursorPos(1, 1)
1253end
1254
1255local function clearDataArea()
1256 term.setBackgroundColor(black)
1257 local line = string.rep(" ", 25)
1258 for i = 5, termY do
1259 term.setCursorPos(26, i)
1260 term.write(line)
1261 end
1262end
1263
1264do
1265 local function sortDeviceList(newDeviceData)
1266 local clientCount = #allClients
1267 if clientCount == 0 then --# the list is empty
1268 allClients[1] = { } --# add it
1269 quietClients[1] = false
1270 for k, v in pairs(newDeviceData) do
1271 allClients[1][k] = v
1272 end
1273 thisCommand = allClients[1].lockState and "LOCKED" or allClients[1].deviceState
1274 clearMonButtons()
1275 else
1276 for i = 1, clientCount do
1277 if client == allClients[i].cc then --# client is already on the list
1278 thisCommand = "WiReQRY"
1279 if newDeviceData.deviceState ~= allClients[i].deviceState then
1280 thisCommand = newDeviceData.deviceState
1281 allClients[i].deviceState = thisCommand
1282 ccUpdate = true
1283 end
1284 if newDeviceData.lockState ~= allClients[i].lockState then
1285 thisCommand = newDeviceData.lockState and "LOCKED" or "UNLOCK"
1286 allClients[i].lockState = newDeviceData.lockState
1287 ccUpdate = true
1288 end
1289 if quietClients[i] then ccUpdate = true end
1290 allClients[i].quietCount = 0
1291 quietClients[i] = false
1292 return
1293 end
1294 end
1295 if clientCount < 20 then
1296 for i = 1, clientCount do
1297 if newDeviceData.name < allClients[i].name then --# alphabetize
1298 table.insert(allClients, i, newDeviceData) --# insert
1299 table.insert(quietClients, i, false)
1300 break
1301 end
1302 if i == clientCount then --# we've reached the end of the list
1303 clientCount = clientCount + 1
1304 allClients[clientCount] = { } --# tack it on
1305 quietClients[clientCount] = false
1306 for k, v in pairs(newDeviceData) do
1307 allClients[clientCount][k] = v
1308 end
1309 break
1310 end
1311 end
1312 thisCommand = allClients[clientCount].lockState and "LOCKED" or allClients[clientCount].deviceState
1313 end
1314 end
1315 ccUpdate = true
1316 numPages = math.max(1, math.ceil(#allClients / 7))
1317 pageNum = math.min(math.max(1, pageNum), numPages)
1318 end
1319
1320 netReceive = function()
1321 local id, newCmdData, success, encryptedMessage, decodedMessage, encKey
1322 while true do
1323 for i = 1, modemCount do
1324 if not rednet.isOpen(modemSides[i]) then rednet.open(modemSides[i]) end
1325 end
1326 id, encryptedMessage = rednet.receive(network)
1327 newCmdData, ccSuccess = { }, false
1328 if type(encryptedMessage) == "string" then
1329 success, decodedMessage = pcall(decode, encryptedMessage)
1330 if success and type(decodedMessage) == "string" then
1331 encKey = thisCC .. "WiRe!Comms" .. tostring(id)
1332 success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1333 if success and type(decryptedMessage) == "string" then
1334 success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1335 if success and type(newCmdData) == "table" and newCmdData.program and newCmdData.program == "WiRe" and newCmdData.cc == id and newCmdData.color == ccSettings.color then
1336 client = id
1337 if newCmdData.deviceState == "OFFLINE" then
1338 for i = 1, #allClients do
1339 if allClients[i].cc == client then
1340 table.remove(allClients, i)
1341 table.remove(quietClients, i)
1342 clearDataArea()
1343 clearMonButtons()
1344 thisCommand = newCmdData.deviceState
1345 ccUpdate = true
1346 break
1347 end
1348 end
1349 else
1350 sortDeviceList(newCmdData)
1351 end
1352 ccSuccess = true
1353 end
1354 end
1355 end
1356 end
1357 if not ccSuccess then
1358 client = nil
1359 thisCommand = "Noise"
1360 end
1361 updateScreens()
1362 end
1363 end
1364end
1365
1366local function netSend(toAll)
1367 local dataPack = textutils.serialize({ program = "WiRe", cc = tonumber(thisCC), color = ccSettings.color, cmd = thisCommand })
1368 for i = 1, modemCount do
1369 if not rednet.isOpen(modemSides[i]) then rednet.open(modemSides[i]) end
1370 end
1371 if toAll then
1372 local encKey = thisCC .. "WiRe!Comms" .. thisCC
1373 local encryptedPackage = encode(encrypt(encKey, dataPack))
1374 rednet.broadcast(encryptedPackage, network)
1375 return true
1376 elseif client then
1377 local encKey = tostring(client) .. "WiRe!Comms" .. thisCC
1378 local encryptedPackage = encode(encrypt(encKey, dataPack))
1379 rednet.send(client, encryptedPackage, network)
1380 return true
1381 end
1382 return false
1383end
1384
1385local function termScreen()
1386 term.setCursorPos(14, 12)
1387 term.setTextColor(white)
1388 local lastClient = client or "Server"
1389 term.write(tostring(lastClient) .. " ")
1390 term.setCursorPos(14, 13)
1391 term.setTextColor(thisCommand == "LOCKED" and red or (validStates[thisCommand][2] or yellow))
1392 term.write(thisCommand .. string.rep(" ", 9 - #thisCommand))
1393 term.setCursorPos(14, 14)
1394 term.setTextColor(ccSuccess and green or red)
1395 term.write(tostring(ccSuccess) .. " ")
1396 term.setCursorPos(26, 3) --# Client list
1397 term.setTextColor(silver)
1398 term.write("Clients (page " .. tostring(pageNum) .. " of " .. tostring(numPages) .. ") ")
1399 local yPos, spacerA, spacerB, spacerC, lockState, devState = 0, string.rep(" ", math.floor(termX / 2)), string.rep(" ", 14), string.rep(" ", 10)
1400 for i = (pageNum * 7) - 6, math.min(pageNum * 7, #allClients) do
1401 yPos, lockState, devState = yPos + 1, allClients[i].lockState, allClients[i].deviceState
1402 term.setCursorPos(26, (yPos * 2) + 3)
1403 term.setTextColor(quietClients[i] and yellow or sky)
1404 term.write(allClients[i].name .. " (" .. tostring(allClients[i].cc) .. ")" .. spacerA)
1405 term.setTextColor(lockState and red or (validStates[devState][2] or yellow))
1406 term.setCursorPos(termX - 8, (yPos * 2) + 3)
1407 term.write(lockState and " LOCKED" or (string.rep(" ", 8 - #devState) .. devState))
1408 term.setCursorPos(27, (yPos * 2) + 4)
1409 term.setTextColor(silver)
1410 if allClients[i].loc.x == "No GPS Fix" then
1411 term.write(allClients[i].loc.x .. spacerB)
1412 else
1413 term.write("GPS: " .. tostring(allClients[i].loc.x) .. "/" .. tostring(allClients[i].loc.y) .. "/" .. tostring(allClients[i].loc.z) .. spacerC)
1414 end
1415 end
1416end
1417
1418do
1419 local labels = {
1420 [1] = { "Name:", 1, 3 };
1421 [2] = { "Note:", 1, 4 };
1422 [3] = { "Group:", 1, 6 };
1423 [4] = { "cc#", 1, 8 };
1424 [5] = { "Modems:", 1, 10 };
1425 [6] = { "Last Client:", 1, 12 };
1426 [7] = { "Last State:", 1, 13 };
1427 [8] = { "Success:", 1, 14 };
1428 [9] = { "Location: x:", 1, 16 };
1429 [10] = { "y:", 11, 17 };
1430 [11] = { "z:", 11, 18 };
1431 }
1432
1433 termScreenStatic = function()
1434 term.setBackgroundColor(black)
1435 term.clear()
1436 local hText = "WiRe Server " .. WiReSver
1437 local spacer = (termX - #hText) / 2
1438 term.setBackgroundColor(blue)
1439 term.setTextColor(white)
1440 term.setCursorPos(1, 1)
1441 term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1442 term.setBackgroundColor(black)
1443 term.setTextColor(silver)
1444 for i = 1, 11 do
1445 term.setCursorPos(labels[i][2], labels[i][3])
1446 term.write(labels[i][1])
1447 end
1448 term.setTextColor(white)
1449 term.setCursorPos(7, 3)
1450 term.write(ccSettings.name)
1451 term.setCursorPos(7, 4)
1452 term.write(ccSettings.note)
1453 term.setCursorPos(5, 8)
1454 term.write(thisCC)
1455 term.setCursorPos(9, 10)
1456 term.write(tostring(modemCount))
1457 term.setCursorPos(14, 16)
1458 term.write(tostring(loc.x))
1459 term.setCursorPos(14, 17)
1460 term.write(tostring(loc.y))
1461 term.setCursorPos(14, 18)
1462 term.write(tostring(loc.z))
1463 term.setTextColor(colorBurst[ccSettings.color][1] or silver)
1464 term.setCursorPos(8, 6)
1465 term.write(ccSettings.color)
1466 end
1467end
1468
1469local function helpScreen()
1470 local hText = "WiRe Server Help"
1471 local xPos, spacer = math.floor(termX / 2), (termX - #hText) / 2
1472 term.setBackgroundColor(white)
1473 term.clear()
1474 term.setBackgroundColor(blue)
1475 term.setTextColor(white)
1476 term.setCursorPos(1, 1)
1477 term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1478 term.setCursorPos(1, 2)
1479 term.setBackgroundColor(gray)
1480 term.write(string.rep(" ", termX))
1481 term.setCursorPos(xPos - 8, 2)
1482 term.write("-- Key Commands --")
1483 term.setCursorPos(1, 7)
1484 term.write(string.rep(" ", termX))
1485 term.setCursorPos(xPos - 8, 7)
1486 term.write("-- Client list --")
1487 term.setCursorPos(1, 13)
1488 term.write(string.rep(" ", termX))
1489 term.setCursorPos(xPos - 10, 13)
1490 term.write("-- Monitor Controls --")
1491 term.setBackgroundColor(white)
1492 term.setTextColor(black)
1493 term.setCursorPos(xPos - 8, 3)
1494 term.write("'q' to quit server")
1495 term.setCursorPos(xPos - 12, 5)
1496 term.write("'F1' to display/exit help")
1497 term.setCursorPos(xPos - 18, 9)
1498 term.write(", < [ { PGUP Go back one page")
1499 term.setCursorPos(xPos - 18, 11)
1500 term.write(". > ] } PGDN Go forward one page")
1501 term.setCursorPos(xPos - 18, 15)
1502 term.write("Touch a switch to toggle its state")
1503 term.setCursorPos(xPos - 18, 17)
1504 term.write("Touch a switch's status line to lock")
1505 term.setCursorPos(xPos - 18, 19)
1506 term.write("Touch a switch's status line to unlock")
1507end
1508
1509updateScreens = function()
1510 if not tArgs[1] and not help then termScreen() end
1511 if #allClients == 0 or (thisCommand ~= "Noise" and ccUpdate) then
1512 monControls()
1513 ccUpdate = false
1514 end
1515end
1516
1517local function shutDown()
1518 kernelState = false
1519 rednet.unhost(network, ccSettings.color)
1520 for i = 1, modemCount do
1521 if rednet.isOpen(modemSides[i]) then rednet.close(modemSides[i]) end
1522 end
1523 clearMon()
1524end
1525
1526local function monTouch()
1527 local halfMon, _, touchSide, posX, posY, continue = monX / 2
1528 while true do
1529 _, touchSide, posX, posY = os.pullEvent("monitor_touch")
1530 if touchSide == monSide then
1531 continue = true
1532 if posY == monY and (posX < math.floor(halfMon) or posX > math.ceil(halfMon)) then
1533 thisCommand = posX < math.floor(halfMon) and "LOCKED" or "UNLOCK"
1534 client = "ALL"
1535 ccSuccess = netSend(true)
1536 ccUpdate = true
1537 continue = false
1538 end
1539 if continue then
1540 for i = 1, #allClients do
1541 if posX >= (i * 8) - 6 and posX <= (i * 8) - 1 then
1542 client = allClients[i].cc
1543 if posY == 8 then
1544 allClients[i].lockState = not allClients[i].lockState
1545 thisCommand = allClients[i].lockState and "LOCKED" or "UNLOCK"
1546 ccSuccess = netSend()
1547 ccUpdate = true
1548 break
1549 elseif posY > 4 and posY < 7 then
1550 if not allClients[i].lockState then
1551 thisCommand = validStates[allClients[i].deviceState][3]
1552 allClients[i].deviceState = thisCommand
1553 ccSuccess = netSend()
1554 ccUpdate = true
1555 end
1556 break
1557 end
1558 end
1559 end
1560 end
1561 if ccUpdate then updateScreens() end
1562 end
1563 end
1564end
1565
1566local function userInput()
1567 local event, data
1568 while true do
1569 event, data = os.pullEvent()
1570 if event == "key" then
1571 if (data == keys.pageUp or data == keys.pageDown) and not help then
1572 pageNum = data == keys.pageUp and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
1573 if pageNum == numPages and numPages > 1 then clearDataArea() end
1574 termScreen()
1575 elseif (data == keys.home or data == keys["end"]) and not help then
1576 pageNum = data == keys.home and 1 or numPages
1577 if pageNum == numPages and numPages > 1 then clearDataArea() end
1578 termScreen()
1579 elseif data == keys.f1 then
1580 help = not help
1581 if help then
1582 helpScreen()
1583 else
1584 termScreenStatic()
1585 termScreen()
1586 end
1587 end
1588 elseif event == "char" then
1589 if (data == "," or data == "<" or data == "[" or data == "{" or data == "." or data == ">" or data == "]" or data == "}") and not help then
1590 pageNum = (data == "," or data == "<" or data == "[" or data == "{") and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
1591 if pageNum == numPages and numPages > 1 then clearDataArea() end
1592 termScreen()
1593 elseif string.lower(data) == "q" then
1594 shutDown()
1595 clearTerm()
1596 term.write("WiRe Server is OFFLINE")
1597 term.setCursorPos(1, 3)
1598 return
1599 end
1600 end
1601 end
1602end
1603
1604local function foregroundShell()
1605 clearTerm()
1606 if fs.exists(tArgs[1]) then
1607 shell.run(table.unpack(tArgs))
1608 clearTerm()
1609 else
1610 term.write(tArgs[1] .. " missing")
1611 term.setCursorPos(1, 3)
1612 end
1613 shutDown()
1614 term.write("WiRe Server is OFFLINE")
1615 term.setCursorPos(1, 5)
1616end
1617
1618local function dataPoller()
1619 local _, timer, clientSilent
1620 while true do
1621 _, timer = os.pullEvent("timer")
1622 if timer == pollTimer and kernelState then
1623 clientSilent = false
1624 for i = 1, #allClients do
1625 if allClients[i].quietCount > 2 then
1626 if not quietClients[i] then
1627 quietClients[i] = true
1628 clientSilent = true
1629 end
1630 else
1631 allClients[i].quietCount = allClients[i].quietCount + 1
1632 end
1633 end
1634 if clientSilent then monControls() end
1635 thisCommand = "WiReQRY"
1636 client = nil
1637 ccSuccess = true
1638 pollTimer = os.startTimer(7)
1639 if not help and not tArgs[1] then termScreen() end
1640 end
1641 end
1642end
1643
1644local function initError(missing, device)
1645 if modemCount > 0 then
1646 for i = 1, modemCount do
1647 if rednet.isOpen(modemSides[i]) then
1648 rednet.close(modemSides[i])
1649 end
1650 end
1651 end
1652 term.clear()
1653 term.setTextColor(red)
1654 term.setCursorPos(1, 2)
1655 print("No " .. missing .. " detected!")
1656 print("WiRe Server REQUIRES")
1657 print(device .. ".")
1658 term.setCursorPos(1, 6)
1659end
1660
1661local function firstRun()
1662 term.clear()
1663 local gotModem = false
1664 for _, side in pairs(rs.getSides()) do
1665 if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
1666 gotModem = true
1667 break
1668 end
1669 end
1670 if not gotModem then return initError("modem", "a modem") end
1671 --# Set server name
1672 term.setCursorPos(2, 2)
1673 term.write("Please name this server")
1674 repeat
1675 term.setCursorPos(2, 4)
1676 local newName = read()
1677 ccSettings.name = newName
1678 until newName ~= ""
1679 --# Set computer label
1680 if not os.getComputerLabel() then os.setComputerLabel(ccSettings.name) end
1681 --# Set server description
1682 term.clear()
1683 term.setCursorPos(2, 2)
1684 term.write("Please type in a short")
1685 term.setCursorPos(2, 3)
1686 term.write("server description")
1687 repeat
1688 term.setCursorPos(2, 5)
1689 local newDesc = read()
1690 ccSettings.note = newDesc
1691 until newDesc ~= ""
1692 --# Select color
1693 local colorWheel = {
1694 P = "Purple";
1695 M = "Magenta";
1696 B = "Blue";
1697 S = "Sky";
1698 C = "Cyan";
1699 E = "Green";
1700 L = "Lime";
1701 R = "Red";
1702 O = "Orange";
1703 Y = "Yellow";
1704 N = "Brown";
1705 K = "Black";
1706 G = "Gray";
1707 I = "Silver";
1708 W = "White";
1709 }
1710 term.clear()
1711 term.setCursorPos(2, 1)
1712 term.write("Please select the network group")
1713 term.setCursorPos(2, 2)
1714 term.write("this server will manage")
1715 local ttY = 2
1716 for k, v in pairs(colorWheel) do
1717 ttY = ttY + 1
1718 term.setCursorPos(2, ttY)
1719 term.write(k .. " = " .. v)
1720 end
1721 repeat
1722 term.setCursorPos(30, 2)
1723 local newColor = string.upper(read())
1724 ccSettings.color = colorWheel[newColor:sub(1, 1)]
1725 until colorWheel[newColor:sub(1, 1)]
1726 --# Should WiRe server get a GPS fix on startup?
1727 term.clear()
1728 term.setCursorPos(2, 1)
1729 term.write("Last question!")
1730 term.setCursorPos(2, 3)
1731 term.write("Do you want WiRe server to")
1732 term.setCursorPos(2, 4)
1733 term.write("aquire a GPS fix on startup? [y/n]")
1734 term.setCursorPos(2, 6)
1735 local getFix = string.lower(read())
1736 ccSettings.getGPSFix = getFix:sub(1, 1) == "y"
1737 if not fs.exists("/data") then fs.makeDir("/data") end
1738 saveData()
1739 return true
1740end
1741
1742term.setBackgroundColor(black)
1743if pocket or turtle then error("Computer REQUIRED.", 0) end
1744if not fs.exists(config) then
1745 if not firstRun() then return end
1746end
1747term.clear()
1748term.setCursorPos(2, 2)
1749term.setTextColor(white)
1750term.write("Ingesting configuration data . . .")
1751local srvConfig = fs.open(config, "r") or error("initMe(): Cannot open " .. config .. " for reading", 0)
1752ccSettings = textutils.unserialize(srvConfig.readAll())
1753srvConfig.close()
1754if ccSettings.getFix ~= nil or not ccSettings.newColors then
1755 if ccSettings.getFix ~= nil then
1756 ccSettings.getGPSFix = ccSettings.getFix
1757 ccSettings.getFix = nil
1758 end
1759 if not ccSettings.newColors then
1760 if ccSettings.color == "lgray" or ccSettings.color == "Light Gray" then
1761 ccSettings.color = "Silver"
1762 elseif ccSettings.color == "lblue" or ccSettings.color == "Light Blue" then
1763 ccSettings.color = "Sky"
1764 end
1765 ccSettings.newColors = true
1766 end
1767 saveData()
1768end
1769term.setCursorPos(2, 4)
1770term.write("Configuring hardware . . .")
1771for _, side in pairs(rs.getSides()) do
1772 if peripheral.isPresent(side) then
1773 if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
1774 mon = peripheral.wrap(side)
1775 monSide = side
1776 elseif peripheral.getType(side) == "modem" then
1777 if peripheral.call(side, "isWireless") then
1778 modemCount = modemCount + 1
1779 modemSides[modemCount] = side
1780 if not rednet.isOpen(side) then rednet.open(side) end
1781 else
1782 for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
1783 if peripheral.getType(name) == "monitor" and peripheral.call(name, "isColor") then
1784 mon = peripheral.wrap(name)
1785 monSide = name
1786 elseif peripheral.getType(name) == "computer" then
1787 local modemFound = false
1788 for i = 1, modemCount do
1789 if modemSides[i] == side then modemFound = true break end
1790 end
1791 if not modemFound then
1792 modemCount = modemCount + 1
1793 modemSides[modemCount] = side
1794 if not rednet.isOpen(side) then rednet.open(side) end
1795 end
1796 end
1797 end
1798 end
1799 end
1800 end
1801end
1802if modemCount == 0 then return initError("modem", "a modem") end
1803if not mon then return initError("monitor", "an Advanced Monitor") end
1804mon.setTextScale(0.5)
1805monX, monY = mon.getSize()
1806clearMon(mwhite)
1807mon.setTextColor(mblack)
1808mon.setCursorPos(2, 3)
1809mon.write("Initializing...")
1810term.setCursorPos(2, 6)
1811term.write("Hosting services...")
1812network = "WiRe" .. ccSettings.color
1813rednet.host(network, ccSettings.color)
1814if ccSettings.getGPSFix then
1815 term.setCursorPos(2, 8)
1816 term.write("Acquiring GPS fix . . .")
1817 loc.x, loc.y, loc.z = gps.locate(2)
1818end
1819if not loc.x then
1820 loc.x, loc.y, loc.z = "No GPS Fix", "No GPS Fix", "No GPS Fix"
1821end
1822ccSuccess = true
1823thisCommand = "init"
1824kernelState = true
1825if tArgs[1] then
1826 term.clear()
1827else
1828 termScreenStatic()
1829end
1830clearMon()
1831--# Monitor Header
1832local labelText = ccSettings.name .. " [" .. ccSettings.color .. "]"
1833local ccNameLen, labelLen, monLabel = #ccSettings.name, #labelText
1834if ccNameLen <= monX and labelLen > monX then
1835 local spacer = (monX - ccNameLen) / 2
1836 monLabel = string.rep(" ", math.floor(spacer)) .. ccSettings.name .. string.rep(" ", math.ceil(spacer))
1837elseif ccNameLen > monX then
1838 monLabel = ccSettings.name:sub(1, monX)
1839else
1840 local spacer = (monX - labelLen) / 2
1841 monLabel = string.rep(" ", math.floor(spacer)) .. labelText .. string.rep(" ", math.ceil(spacer))
1842end
1843mon.setBackgroundColor(colorBurst[ccSettings.color][2] or mgray)
1844mon.setTextColor(colorBurst[ccSettings.color][3] or mcyan)
1845mon.setCursorPos(1, 1)
1846mon.write(monLabel)
1847--# Monitor Footer
1848local xPos, xPos2 = math.floor(monX / 4), monX > 15 and math.ceil(monX / 2) + 8 or math.ceil(monX / 2) + 2
1849mon.setCursorPos(1, monY)
1850mon.setBackgroundColor(mgray)
1851mon.write(string.rep(" ", monX))
1852mon.setTextColor(mred)
1853mon.setCursorPos(xPos - 2, monY)
1854mon.write("LOCK")
1855mon.setTextColor(mgreen)
1856mon.setCursorPos(monX > 36 and monX - xPos - 3 or xPos2, monY)
1857mon.write("UNLOCK")
1858mon.setCursorPos(math.floor(monX / 2) - 1, monY)
1859mon.setBackgroundColor(mblack)
1860mon.write(" ")
1861updateScreens()
1862pollTimer = os.startTimer(1)
1863if tArgs[1] then
1864 parallel.waitForAny(netReceive, dataPoller, monTouch, foregroundShell)
1865else
1866 parallel.waitForAny(netReceive, dataPoller, monTouch, userInput)
1867end