· 5 years ago · Oct 22, 2020, 05:04 PM
1--[[ cc10 Remote ]]--
2--[[ by Dog ]]--
3--[[ aka HydrantHunter ]]--
4--[[ with assistance ]]--
5--[[ from CometWolf ]]--
6--[[ pastebin mbEHZZnh ]]--
7local cc10Rver = "2.0.04"
8--[[
9Tested with/requires:
10 - Minecraft 1.6.4+
11 - ComputerCraft 1.63+
12 - 1 Advanced Wireless Pocket Computer
13 - cc10host running on a Computer (standard or advanced) with a wireless modem
14 or a Wireless Turtle (standard or advanced)
15
16Special thanks to: Anavrins (pbkdf2/sha256 hashing)
17 SquidDev (AES encryption/decryption)
18 Alex Kloss (base64 encoder/decoder)
19]]--
20--# AUTOMATIC/STATIC CONFIGURATION
21--# Default Settings
22local termX, termY = term.getSize()
23local netSide, runState, thisCC = "none", "init", tostring(os.getComputerID())
24local kernelState, timerDelay, inputting = true, false, false
25local drawCLI, netReceive, userInput, pollTimer, tempState
26--# Device List
27local allDevices, filteredDevices = { }, { }
28local numPages, pageNum = 1, 1
29local uFilter = false
30local thisDevice, cc10host
31local cc10Types = {
32 A = { "Analog", "UA" };
33 UA = { "U-Analog", "D" };
34 D = { "Digital", "UD" };
35 UD = { "U-Digital", "PS" };
36 PS = { "Pulsed-S", "PL" };
37 PL = { "Pulsed-L", "P3" };
38 P3 = { "Pulsed-3", "A" };
39}
40local colorBurst = {
41 G = { order = 1, text = "Green", color = colors.green };
42 S = { order = 2, text = "Sky", color = colors.lightBlue };
43 B = { order = 3, text = "Blue", color = colors.blue };
44 P = { order = 4, text = "Purple", color = colors.purple };
45 R = { order = 5, text = "Red", color = colors.red };
46 O = { order = 6, text = "Orange", color = colors.orange };
47 N = { order = 7, text = "Brown", color = colors.brown };
48 I = { order = 8, text = "Silver", color = colors.lightGray };
49}
50--# Color Definitions
51local white = colors.white
52local black = colors.black
53local silver = colors.lightGray
54local gray = colors.gray
55local brown = colors.brown
56local yellow = colors.yellow
57local orange = colors.orange
58local red = colors.red
59local magenta = colors.magenta
60local purple = colors.purple
61local blue = colors.blue
62local sky = colors.lightBlue
63local cyan = colors.cyan
64local lime = colors.lime
65local green = colors.green
66--local colorX, word
67--local curX, curY
68--# END AUTOMATIC/STATIC CONFIGURATION
69
70-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
71-- licensed under the terms of the LGPL2
72-- http://lua-users.org/wiki/BaseSixtyFour
73-- character table string
74local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
75-- encoding
76function encode(data)
77 return ((data:gsub('.', function(x)
78 local r,b='',x:byte()
79 for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
80 return r;
81 end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
82 if (#x < 6) then return '' end
83 local c=0
84 for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
85 return b:sub(c+1,c+1)
86 end)..({ '', '==', '=' })[#data%3+1])
87end
88-- decoding
89function decode(data)
90 data = string.gsub(data, '[^'..b..'=]', '')
91 return (data:gsub('.', function(x)
92 if (x == '=') then return '' end
93 local r,f='',(b:find(x)-1)
94 for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
95 return r;
96 end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
97 if (#x ~= 8) then return '' end
98 local c=0
99 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
100 return string.char(c)
101 end))
102end
103
104-- AES Lua implementation by SquidDev
105-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
106local encrypt, decrypt
107do
108 local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
109 local bit=_W(function(_ENV, ...)
110 --[[
111 This bit API is designed to cope with unsigned integers instead of normal integers
112 To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
113 These are written in long form because no constant folding.
114 ]]
115 local floor = math.floor
116 local lshift, rshift
117
118 rshift = function(a,disp)
119 return floor(a % 4294967296 / 2^disp)
120 end
121
122 lshift = function(a,disp)
123 return (a * 2^disp) % 4294967296
124 end
125
126 return {
127 -- bit operations
128 bnot = bit32 and bit32.bnot or bit.bnot,
129 band = bit32 and bit32.band or bit.band,
130 bor = bit32 and bit32.bor or bit.bor,
131 bxor = bit32 and bit32.bxor or bit.bxor,
132 rshift = rshift,
133 lshift = lshift,
134 }
135 end)
136
137 local gf=_W(function(_ENV, ...)
138 -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
139 local bxor = bit32 and bit32.bxor or bit.bxor
140 local lshift = bit.lshift
141 -- private data of gf
142 local n = 0x100
143 local ord = 0xff
144 local irrPolynom = 0x11b
145 local exp = {}
146 local log = {}
147 --
148 -- add two polynoms (its simply xor)
149 --
150 local function add(operand1, operand2)
151 return bxor(operand1,operand2)
152 end
153 --
154 -- subtract two polynoms (same as addition)
155 --
156 local function sub(operand1, operand2)
157 return bxor(operand1,operand2)
158 end
159 --
160 -- inverts element
161 -- a^(-1) = g^(order - log(a))
162 --
163 local function invert(operand)
164 -- special case for 1
165 if (operand == 1) then
166 return 1
167 end
168 -- normal invert
169 local exponent = ord - log[operand]
170 return exp[exponent]
171 end
172 --
173 -- multiply two elements using a logarithm table
174 -- a*b = g^(log(a)+log(b))
175 --
176 local function mul(operand1, operand2)
177 if (operand1 == 0 or operand2 == 0) then
178 return 0
179 end
180 local exponent = log[operand1] + log[operand2]
181 if (exponent >= ord) then
182 exponent = exponent - ord
183 end
184 return exp[exponent]
185 end
186 --
187 -- divide two elements
188 -- a/b = g^(log(a)-log(b))
189 --
190 local function div(operand1, operand2)
191 if (operand1 == 0) then
192 return 0
193 end
194 -- TODO: exception if operand2 == 0
195 local exponent = log[operand1] - log[operand2]
196 if (exponent < 0) then
197 exponent = exponent + ord
198 end
199 return exp[exponent]
200 end
201 --
202 -- print logarithmic table
203 --
204 local function printLog()
205 for i = 1, n do
206 print("log(", i-1, ")=", log[i-1])
207 end
208 end
209 --
210 -- print exponentiation table
211 --
212 local function printExp()
213 for i = 1, n do
214 print("exp(", i-1, ")=", exp[i-1])
215 end
216 end
217 --
218 -- calculate logarithmic and exponentiation table
219 --
220 local function initMulTable()
221 local a = 1
222 for i = 0,ord-1 do
223 exp[i] = a
224 log[a] = i
225 -- multiply with generator x+1 -> left shift + 1
226 a = bxor(lshift(a, 1), a)
227 -- if a gets larger than order, reduce modulo irreducible polynom
228 if a > ord then
229 a = sub(a, irrPolynom)
230 end
231 end
232 end
233
234 initMulTable()
235
236 return {
237 add = add,
238 sub = sub,
239 invert = invert,
240 mul = mul,
241 div = div,
242 printLog = printLog,
243 printExp = printExp,
244 }
245 end)
246
247 util=_W(function(_ENV, ...)
248 -- Cache some bit operators
249 local bxor = bit.bxor
250 local rshift = bit.rshift
251 local band = bit.band
252 local lshift = bit.lshift
253 local sleepCheckIn
254 --
255 -- calculate the parity of one byte
256 --
257 local function byteParity(byte)
258 byte = bxor(byte, rshift(byte, 4))
259 byte = bxor(byte, rshift(byte, 2))
260 byte = bxor(byte, rshift(byte, 1))
261 return band(byte, 1)
262 end
263 --
264 -- get byte at position index
265 --
266 local function getByte(number, index)
267 return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
268 end
269 --
270 -- put number into int at position index
271 --
272 local function putByte(number, index)
273 return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
274 end
275 --
276 -- convert byte array to int array
277 --
278 local function bytesToInts(bytes, start, n)
279 local ints = {}
280 for i = 0, n - 1 do
281 ints[i + 1] =
282 putByte(bytes[start + (i*4)], 3) +
283 putByte(bytes[start + (i*4) + 1], 2) +
284 putByte(bytes[start + (i*4) + 2], 1) +
285 putByte(bytes[start + (i*4) + 3], 0)
286 if n % 10000 == 0 then sleepCheckIn() end
287 end
288 return ints
289 end
290 --
291 -- convert int array to byte array
292 --
293 local function intsToBytes(ints, output, outputOffset, n)
294 n = n or #ints
295 for i = 0, n - 1 do
296 for j = 0,3 do
297 output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
298 end
299 if n % 10000 == 0 then sleepCheckIn() end
300 end
301 return output
302 end
303 --
304 -- convert bytes to hexString
305 --
306 local function bytesToHex(bytes)
307 local hexBytes = ""
308 for i,byte in ipairs(bytes) do
309 hexBytes = hexBytes .. string.format("%02x ", byte)
310 end
311 return hexBytes
312 end
313
314 local function hexToBytes(bytes)
315 local out = {}
316 for i = 1, #bytes, 2 do
317 out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
318 end
319 return out
320 end
321 --
322 -- convert data to hex string
323 --
324 local function toHexString(data)
325 local type = type(data)
326 if (type == "number") then
327 return string.format("%08x",data)
328 elseif (type == "table") then
329 return bytesToHex(data)
330 elseif (type == "string") then
331 local bytes = {string.byte(data, 1, #data)}
332 return bytesToHex(bytes)
333 else
334 return data
335 end
336 end
337
338 local function padByteString(data)
339 local dataLength = #data
340 local random1 = math.random(0,255)
341 local random2 = math.random(0,255)
342 local prefix = string.char(random1,
343 random2,
344 random1,
345 random2,
346 getByte(dataLength, 3),
347 getByte(dataLength, 2),
348 getByte(dataLength, 1),
349 getByte(dataLength, 0)
350 )
351 data = prefix .. data
352 local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
353 for i=1,paddingLength do
354 padding = padding .. string.char(math.random(0,255))
355 end
356 return data .. padding
357 end
358
359 local function properlyDecrypted(data)
360 local random = {string.byte(data,1,4)}
361 if (random[1] == random[3] and random[2] == random[4]) then
362 return true
363 end
364 return false
365 end
366
367 local function unpadByteString(data)
368 if (not properlyDecrypted(data)) then
369 return nil
370 end
371 local dataLength = putByte(string.byte(data,5), 3)
372 + putByte(string.byte(data,6), 2)
373 + putByte(string.byte(data,7), 1)
374 + putByte(string.byte(data,8), 0)
375 return string.sub(data,9,8+dataLength)
376 end
377
378 local function xorIV(data, iv)
379 for i = 1,16 do
380 data[i] = bxor(data[i], iv[i])
381 end
382 end
383
384 local function increment(data)
385 local i = 16
386 while true do
387 local value = data[i] + 1
388 if value >= 256 then
389 data[i] = value - 256
390 i = (i - 2) % 16 + 1
391 else
392 data[i] = value
393 break
394 end
395 end
396 end
397
398 -- Called every encryption cycle
399 local push, pull, time = os.queueEvent, coroutine.yield, os.time
400 local oldTime = time()
401 local function sleepCheckIn()
402 local newTime = time()
403 if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
404 oldTime = newTime
405 push("sleep")
406 pull("sleep")
407 end
408 end
409
410 local function getRandomData(bytes)
411 local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
412 local result = {}
413 for i=1,bytes do
414 insert(result, random(0,255))
415 if i % 10240 == 0 then sleep() end
416 end
417 return result
418 end
419
420 local function getRandomString(bytes)
421 local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
422 local result = {}
423 for i=1,bytes do
424 insert(result, char(random(0,255)))
425 if i % 10240 == 0 then sleep() end
426 end
427 return table.concat(result)
428 end
429
430 return {
431 byteParity = byteParity,
432 getByte = getByte,
433 putByte = putByte,
434 bytesToInts = bytesToInts,
435 intsToBytes = intsToBytes,
436 bytesToHex = bytesToHex,
437 hexToBytes = hexToBytes,
438 toHexString = toHexString,
439 padByteString = padByteString,
440 properlyDecrypted = properlyDecrypted,
441 unpadByteString = unpadByteString,
442 xorIV = xorIV,
443 increment = increment,
444 sleepCheckIn = sleepCheckIn,
445 getRandomData = getRandomData,
446 getRandomString = getRandomString,
447 }
448 end)
449
450 aes=_W(function(_ENV, ...)
451 -- Implementation of AES with nearly pure lua
452 -- AES with lua is slow, really slow :-)
453 local putByte = util.putByte
454 local getByte = util.getByte
455 -- some constants
456 local ROUNDS = 'rounds'
457 local KEY_TYPE = "type"
458 local ENCRYPTION_KEY=1
459 local DECRYPTION_KEY=2
460 -- aes SBOX
461 local SBox = {}
462 local iSBox = {}
463 -- aes tables
464 local table0 = {}
465 local table1 = {}
466 local table2 = {}
467 local table3 = {}
468 local tableInv0 = {}
469 local tableInv1 = {}
470 local tableInv2 = {}
471 local tableInv3 = {}
472 -- round constants
473 local rCon = {
474 0x01000000,
475 0x02000000,
476 0x04000000,
477 0x08000000,
478 0x10000000,
479 0x20000000,
480 0x40000000,
481 0x80000000,
482 0x1b000000,
483 0x36000000,
484 0x6c000000,
485 0xd8000000,
486 0xab000000,
487 0x4d000000,
488 0x9a000000,
489 0x2f000000,
490 }
491 --
492 -- affine transformation for calculating the S-Box of AES
493 --
494 local function affinMap(byte)
495 mask = 0xf8
496 result = 0
497 for i = 1,8 do
498 result = bit.lshift(result,1)
499 parity = util.byteParity(bit.band(byte,mask))
500 result = result + parity
501 -- simulate roll
502 lastbit = bit.band(mask, 1)
503 mask = bit.band(bit.rshift(mask, 1),0xff)
504 mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
505 end
506 return bit.bxor(result, 0x63)
507 end
508 --
509 -- calculate S-Box and inverse S-Box of AES
510 -- apply affine transformation to inverse in finite field 2^8
511 --
512 local function calcSBox()
513 for i = 0, 255 do
514 inverse = i ~= 0 and gf.invert(i) or i
515 mapped = affinMap(inverse)
516 SBox[i] = mapped
517 iSBox[mapped] = i
518 end
519 end
520 --
521 -- Calculate round tables
522 -- round tables are used to calculate shiftRow, MixColumn and SubBytes
523 -- with 4 table lookups and 4 xor operations.
524 --
525 local function calcRoundTables()
526 for x = 0,255 do
527 byte = SBox[x]
528 table0[x] = putByte(gf.mul(0x03, byte), 0)
529 + putByte( byte , 1)
530 + putByte( byte , 2)
531 + putByte(gf.mul(0x02, byte), 3)
532 table1[x] = putByte( byte , 0)
533 + putByte( byte , 1)
534 + putByte(gf.mul(0x02, byte), 2)
535 + putByte(gf.mul(0x03, byte), 3)
536 table2[x] = putByte( byte , 0)
537 + putByte(gf.mul(0x02, byte), 1)
538 + putByte(gf.mul(0x03, byte), 2)
539 + putByte( byte , 3)
540 table3[x] = putByte(gf.mul(0x02, byte), 0)
541 + putByte(gf.mul(0x03, byte), 1)
542 + putByte( byte , 2)
543 + putByte( byte , 3)
544 end
545 end
546 --
547 -- Calculate inverse round tables
548 -- does the inverse of the normal roundtables for the equivalent
549 -- decryption algorithm.
550 --
551 local function calcInvRoundTables()
552 for x = 0,255 do
553 byte = iSBox[x]
554 tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
555 + putByte(gf.mul(0x0d, byte), 1)
556 + putByte(gf.mul(0x09, byte), 2)
557 + putByte(gf.mul(0x0e, byte), 3)
558 tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
559 + putByte(gf.mul(0x09, byte), 1)
560 + putByte(gf.mul(0x0e, byte), 2)
561 + putByte(gf.mul(0x0b, byte), 3)
562 tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
563 + putByte(gf.mul(0x0e, byte), 1)
564 + putByte(gf.mul(0x0b, byte), 2)
565 + putByte(gf.mul(0x0d, byte), 3)
566 tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
567 + putByte(gf.mul(0x0b, byte), 1)
568 + putByte(gf.mul(0x0d, byte), 2)
569 + putByte(gf.mul(0x09, byte), 3)
570 end
571 end
572 --
573 -- rotate word: 0xaabbccdd gets 0xbbccddaa
574 -- used for key schedule
575 --
576 local function rotWord(word)
577 local tmp = bit.band(word,0xff000000)
578 return (bit.lshift(word,8) + bit.rshift(tmp,24))
579 end
580 --
581 -- replace all bytes in a word with the SBox.
582 -- used for key schedule
583 --
584 local function subWord(word)
585 return putByte(SBox[getByte(word,0)],0)
586 + putByte(SBox[getByte(word,1)],1)
587 + putByte(SBox[getByte(word,2)],2)
588 + putByte(SBox[getByte(word,3)],3)
589 end
590 --
591 -- generate key schedule for aes encryption
592 --
593 -- returns table with all round keys and
594 -- the necessary number of rounds saved in [ROUNDS]
595 --
596 local function expandEncryptionKey(key)
597 local keySchedule = {}
598 local keyWords = math.floor(#key / 4)
599 if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
600 error("Invalid key size: " .. tostring(keyWords))
601 return nil
602 end
603 keySchedule[ROUNDS] = keyWords + 6
604 keySchedule[KEY_TYPE] = ENCRYPTION_KEY
605 for i = 0,keyWords - 1 do
606 keySchedule[i] = putByte(key[i*4+1], 3)
607 + putByte(key[i*4+2], 2)
608 + putByte(key[i*4+3], 1)
609 + putByte(key[i*4+4], 0)
610 end
611 for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
612 local tmp = keySchedule[i-1]
613 if ( i % keyWords == 0) then
614 tmp = rotWord(tmp)
615 tmp = subWord(tmp)
616 local index = math.floor(i/keyWords)
617 tmp = bit.bxor(tmp,rCon[index])
618 elseif (keyWords > 6 and i % keyWords == 4) then
619 tmp = subWord(tmp)
620 end
621 keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
622 end
623 return keySchedule
624 end
625 --
626 -- Inverse mix column
627 -- used for key schedule of decryption key
628 --
629 local function invMixColumnOld(word)
630 local b0 = getByte(word,3)
631 local b1 = getByte(word,2)
632 local b2 = getByte(word,1)
633 local b3 = getByte(word,0)
634 return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
635 gf.mul(0x0d, b2)),
636 gf.mul(0x09, b3)),
637 gf.mul(0x0e, b0)),3)
638 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
639 gf.mul(0x0d, b3)),
640 gf.mul(0x09, b0)),
641 gf.mul(0x0e, b1)),2)
642 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
643 gf.mul(0x0d, b0)),
644 gf.mul(0x09, b1)),
645 gf.mul(0x0e, b2)),1)
646 + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
647 gf.mul(0x0d, b1)),
648 gf.mul(0x09, b2)),
649 gf.mul(0x0e, b3)),0)
650 end
651 --
652 -- Optimized inverse mix column
653 -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
654 -- TODO: make it work
655 --
656 local function invMixColumn(word)
657 local b0 = getByte(word,3)
658 local b1 = getByte(word,2)
659 local b2 = getByte(word,1)
660 local b3 = getByte(word,0)
661 local t = bit.bxor(b3,b2)
662 local u = bit.bxor(b1,b0)
663 local v = bit.bxor(t,u)
664 v = bit.bxor(v,gf.mul(0x08,v))
665 w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
666 v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
667 return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
668 + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1)
669 + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
670 + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3)
671 end
672 --
673 -- generate key schedule for aes decryption
674 --
675 -- uses key schedule for aes encryption and transforms each
676 -- key by inverse mix column.
677 --
678 local function expandDecryptionKey(key)
679 local keySchedule = expandEncryptionKey(key)
680 if (keySchedule == nil) then
681 return nil
682 end
683 keySchedule[KEY_TYPE] = DECRYPTION_KEY
684 for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
685 keySchedule[i] = invMixColumnOld(keySchedule[i])
686 end
687 return keySchedule
688 end
689 --
690 -- xor round key to state
691 --
692 local function addRoundKey(state, key, round)
693 for i = 0, 3 do
694 state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
695 end
696 end
697 --
698 -- do encryption round (ShiftRow, SubBytes, MixColumn together)
699 --
700 local function doRound(origState, dstState)
701 dstState[1] = bit.bxor(bit.bxor(bit.bxor(
702 table0[getByte(origState[1],3)],
703 table1[getByte(origState[2],2)]),
704 table2[getByte(origState[3],1)]),
705 table3[getByte(origState[4],0)])
706 dstState[2] = bit.bxor(bit.bxor(bit.bxor(
707 table0[getByte(origState[2],3)],
708 table1[getByte(origState[3],2)]),
709 table2[getByte(origState[4],1)]),
710 table3[getByte(origState[1],0)])
711 dstState[3] = bit.bxor(bit.bxor(bit.bxor(
712 table0[getByte(origState[3],3)],
713 table1[getByte(origState[4],2)]),
714 table2[getByte(origState[1],1)]),
715 table3[getByte(origState[2],0)])
716 dstState[4] = bit.bxor(bit.bxor(bit.bxor(
717 table0[getByte(origState[4],3)],
718 table1[getByte(origState[1],2)]),
719 table2[getByte(origState[2],1)]),
720 table3[getByte(origState[3],0)])
721 end
722 --
723 -- do last encryption round (ShiftRow and SubBytes)
724 --
725 local function doLastRound(origState, dstState)
726 dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
727 + putByte(SBox[getByte(origState[2],2)], 2)
728 + putByte(SBox[getByte(origState[3],1)], 1)
729 + putByte(SBox[getByte(origState[4],0)], 0)
730 dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
731 + putByte(SBox[getByte(origState[3],2)], 2)
732 + putByte(SBox[getByte(origState[4],1)], 1)
733 + putByte(SBox[getByte(origState[1],0)], 0)
734 dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
735 + putByte(SBox[getByte(origState[4],2)], 2)
736 + putByte(SBox[getByte(origState[1],1)], 1)
737 + putByte(SBox[getByte(origState[2],0)], 0)
738 dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
739 + putByte(SBox[getByte(origState[1],2)], 2)
740 + putByte(SBox[getByte(origState[2],1)], 1)
741 + putByte(SBox[getByte(origState[3],0)], 0)
742 end
743 --
744 -- do decryption round
745 --
746 local function doInvRound(origState, dstState)
747 dstState[1] = bit.bxor(bit.bxor(bit.bxor(
748 tableInv0[getByte(origState[1],3)],
749 tableInv1[getByte(origState[4],2)]),
750 tableInv2[getByte(origState[3],1)]),
751 tableInv3[getByte(origState[2],0)])
752 dstState[2] = bit.bxor(bit.bxor(bit.bxor(
753 tableInv0[getByte(origState[2],3)],
754 tableInv1[getByte(origState[1],2)]),
755 tableInv2[getByte(origState[4],1)]),
756 tableInv3[getByte(origState[3],0)])
757 dstState[3] = bit.bxor(bit.bxor(bit.bxor(
758 tableInv0[getByte(origState[3],3)],
759 tableInv1[getByte(origState[2],2)]),
760 tableInv2[getByte(origState[1],1)]),
761 tableInv3[getByte(origState[4],0)])
762 dstState[4] = bit.bxor(bit.bxor(bit.bxor(
763 tableInv0[getByte(origState[4],3)],
764 tableInv1[getByte(origState[3],2)]),
765 tableInv2[getByte(origState[2],1)]),
766 tableInv3[getByte(origState[1],0)])
767 end
768 --
769 -- do last decryption round
770 --
771 local function doInvLastRound(origState, dstState)
772 dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
773 + putByte(iSBox[getByte(origState[4],2)], 2)
774 + putByte(iSBox[getByte(origState[3],1)], 1)
775 + putByte(iSBox[getByte(origState[2],0)], 0)
776 dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
777 + putByte(iSBox[getByte(origState[1],2)], 2)
778 + putByte(iSBox[getByte(origState[4],1)], 1)
779 + putByte(iSBox[getByte(origState[3],0)], 0)
780 dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
781 + putByte(iSBox[getByte(origState[2],2)], 2)
782 + putByte(iSBox[getByte(origState[1],1)], 1)
783 + putByte(iSBox[getByte(origState[4],0)], 0)
784 dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
785 + putByte(iSBox[getByte(origState[3],2)], 2)
786 + putByte(iSBox[getByte(origState[2],1)], 1)
787 + putByte(iSBox[getByte(origState[1],0)], 0)
788 end
789 --
790 -- encrypts 16 Bytes
791 -- key encryption key schedule
792 -- input array with input data
793 -- inputOffset start index for input
794 -- output array for encrypted data
795 -- outputOffset start index for output
796 --
797 local function encrypt(key, input, inputOffset, output, outputOffset)
798 --default parameters
799 inputOffset = inputOffset or 1
800 output = output or {}
801 outputOffset = outputOffset or 1
802 local state = {}
803 local tmpState = {}
804 if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
805 error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
806 return
807 end
808 state = util.bytesToInts(input, inputOffset, 4)
809 addRoundKey(state, key, 0)
810 local round = 1
811 while (round < key[ROUNDS] - 1) do
812 -- do a double round to save temporary assignments
813 doRound(state, tmpState)
814 addRoundKey(tmpState, key, round)
815 round = round + 1
816 doRound(tmpState, state)
817 addRoundKey(state, key, round)
818 round = round + 1
819 end
820 doRound(state, tmpState)
821 addRoundKey(tmpState, key, round)
822 round = round +1
823 doLastRound(tmpState, state)
824 addRoundKey(state, key, round)
825 util.sleepCheckIn()
826 return util.intsToBytes(state, output, outputOffset)
827 end
828 --
829 -- decrypt 16 bytes
830 -- key decryption key schedule
831 -- input array with input data
832 -- inputOffset start index for input
833 -- output array for decrypted data
834 -- outputOffset start index for output
835 ---
836 local function decrypt(key, input, inputOffset, output, outputOffset)
837 -- default arguments
838 inputOffset = inputOffset or 1
839 output = output or {}
840 outputOffset = outputOffset or 1
841 local state = {}
842 local tmpState = {}
843 if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
844 error("No decryption key: " .. tostring(key[KEY_TYPE]))
845 return
846 end
847 state = util.bytesToInts(input, inputOffset, 4)
848 addRoundKey(state, key, key[ROUNDS])
849 local round = key[ROUNDS] - 1
850 while (round > 2) do
851 -- do a double round to save temporary assignments
852 doInvRound(state, tmpState)
853 addRoundKey(tmpState, key, round)
854 round = round - 1
855 doInvRound(tmpState, state)
856 addRoundKey(state, key, round)
857 round = round - 1
858 end
859 doInvRound(state, tmpState)
860 addRoundKey(tmpState, key, round)
861 round = round - 1
862 doInvLastRound(tmpState, state)
863 addRoundKey(state, key, round)
864 util.sleepCheckIn()
865 return util.intsToBytes(state, output, outputOffset)
866 end
867
868 -- calculate all tables when loading this file
869 calcSBox()
870 calcRoundTables()
871 calcInvRoundTables()
872
873 return {
874 ROUNDS = ROUNDS,
875 KEY_TYPE = KEY_TYPE,
876 ENCRYPTION_KEY = ENCRYPTION_KEY,
877 DECRYPTION_KEY = DECRYPTION_KEY,
878 expandEncryptionKey = expandEncryptionKey,
879 expandDecryptionKey = expandDecryptionKey,
880 encrypt = encrypt,
881 decrypt = decrypt,
882 }
883 end)
884
885 local buffer=_W(function(_ENV, ...)
886 local function new ()
887 return {}
888 end
889
890 local function addString (stack, s)
891 table.insert(stack, s)
892 end
893
894 local function toString (stack)
895 return table.concat(stack)
896 end
897
898 return {
899 new = new,
900 addString = addString,
901 toString = toString,
902 }
903 end)
904
905 ciphermode=_W(function(_ENV, ...)
906 local public = {}
907 --
908 -- Encrypt strings
909 -- key - byte array with key
910 -- string - string to encrypt
911 -- modefunction - function for cipher mode to use
912 --
913 local random, unpack = math.random, unpack or table.unpack
914 function public.encryptString(key, data, modeFunction, iv)
915 if iv then
916 local ivCopy = {}
917 for i = 1, 16 do ivCopy[i] = iv[i] end
918 iv = ivCopy
919 else
920 iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
921 end
922 local keySched = aes.expandEncryptionKey(key)
923 local encryptedData = buffer.new()
924 for i = 1, #data/16 do
925 local offset = (i-1)*16 + 1
926 local byteData = {string.byte(data,offset,offset +15)}
927 iv = modeFunction(keySched, byteData, iv)
928 buffer.addString(encryptedData, string.char(unpack(byteData)))
929 end
930 return buffer.toString(encryptedData)
931 end
932 --
933 -- the following 4 functions can be used as
934 -- modefunction for encryptString
935 --
936 -- Electronic code book mode encrypt function
937 function public.encryptECB(keySched, byteData, iv)
938 aes.encrypt(keySched, byteData, 1, byteData, 1)
939 end
940
941 -- Cipher block chaining mode encrypt function
942 function public.encryptCBC(keySched, byteData, iv)
943 util.xorIV(byteData, iv)
944 aes.encrypt(keySched, byteData, 1, byteData, 1)
945 return byteData
946 end
947
948 -- Output feedback mode encrypt function
949 function public.encryptOFB(keySched, byteData, iv)
950 aes.encrypt(keySched, iv, 1, iv, 1)
951 util.xorIV(byteData, iv)
952 return iv
953 end
954
955 -- Cipher feedback mode encrypt function
956 function public.encryptCFB(keySched, byteData, iv)
957 aes.encrypt(keySched, iv, 1, iv, 1)
958 util.xorIV(byteData, iv)
959 return byteData
960 end
961
962 function public.encryptCTR(keySched, byteData, iv)
963 local nextIV = {}
964 for j = 1, 16 do nextIV[j] = iv[j] end
965 aes.encrypt(keySched, iv, 1, iv, 1)
966 util.xorIV(byteData, iv)
967 util.increment(nextIV)
968 return nextIV
969 end
970 --
971 -- Decrypt strings
972 -- key - byte array with key
973 -- string - string to decrypt
974 -- modefunction - function for cipher mode to use
975 --
976 function public.decryptString(key, data, modeFunction, iv)
977 if iv then
978 local ivCopy = {}
979 for i = 1, 16 do ivCopy[i] = iv[i] end
980 iv = ivCopy
981 else
982 iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
983 end
984 local keySched
985 if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
986 keySched = aes.expandEncryptionKey(key)
987 else
988 keySched = aes.expandDecryptionKey(key)
989 end
990 local decryptedData = buffer.new()
991 for i = 1, #data/16 do
992 local offset = (i-1)*16 + 1
993 local byteData = {string.byte(data,offset,offset +15)}
994 iv = modeFunction(keySched, byteData, iv)
995 buffer.addString(decryptedData, string.char(unpack(byteData)))
996 end
997 return buffer.toString(decryptedData)
998 end
999 --
1000 -- the following 4 functions can be used as
1001 -- modefunction for decryptString
1002 --
1003 -- Electronic code book mode decrypt function
1004 function public.decryptECB(keySched, byteData, iv)
1005 aes.decrypt(keySched, byteData, 1, byteData, 1)
1006 return iv
1007 end
1008
1009 -- Cipher block chaining mode decrypt function
1010 function public.decryptCBC(keySched, byteData, iv)
1011 local nextIV = {}
1012 for j = 1, 16 do nextIV[j] = byteData[j] end
1013 aes.decrypt(keySched, byteData, 1, byteData, 1)
1014 util.xorIV(byteData, iv)
1015 return nextIV
1016 end
1017
1018 -- Output feedback mode decrypt function
1019 function public.decryptOFB(keySched, byteData, iv)
1020 aes.encrypt(keySched, iv, 1, iv, 1)
1021 util.xorIV(byteData, iv)
1022 return iv
1023 end
1024
1025 -- Cipher feedback mode decrypt function
1026 function public.decryptCFB(keySched, byteData, iv)
1027 local nextIV = {}
1028 for j = 1, 16 do nextIV[j] = byteData[j] end
1029 aes.encrypt(keySched, iv, 1, iv, 1)
1030 util.xorIV(byteData, iv)
1031 return nextIV
1032 end
1033
1034 public.decryptCTR = public.encryptCTR
1035 return public
1036 end)
1037
1038 -- Simple API for encrypting strings.
1039 --
1040 AES128 = 16
1041 AES192 = 24
1042 AES256 = 32
1043 ECBMODE = 1
1044 CBCMODE = 2
1045 OFBMODE = 3
1046 CFBMODE = 4
1047 CTRMODE = 4
1048
1049 local function pwToKey(password, keyLength, iv)
1050 local padLength = keyLength
1051 if (keyLength == AES192) then
1052 padLength = 32
1053 end
1054 if (padLength > #password) then
1055 local postfix = ""
1056 for i = 1,padLength - #password do
1057 postfix = postfix .. string.char(0)
1058 end
1059 password = password .. postfix
1060 else
1061 password = string.sub(password, 1, padLength)
1062 end
1063 local pwBytes = {string.byte(password,1,#password)}
1064 password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
1065 password = string.sub(password, 1, keyLength)
1066 return {string.byte(password,1,#password)}
1067 end
1068 --
1069 -- Encrypts string data with password password.
1070 -- password - the encryption key is generated from this string
1071 -- data - string to encrypt (must not be too large)
1072 -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1073 -- mode - mode of encryption: ecb, cbc(default), ofb, cfb
1074 --
1075 -- mode and keyLength must be the same for encryption and decryption.
1076 --
1077 function encrypt(password, data, keyLength, mode, iv)
1078 assert(password ~= nil, "Empty password.")
1079 assert(data ~= nil, "Empty data.")
1080 local mode = mode or CBCMODE
1081 local keyLength = keyLength or AES128
1082 local key = pwToKey(password, keyLength, iv)
1083 local paddedData = util.padByteString(data)
1084 if mode == ECBMODE then
1085 return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
1086 elseif mode == CBCMODE then
1087 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
1088 elseif mode == OFBMODE then
1089 return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
1090 elseif mode == CFBMODE then
1091 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
1092 elseif mode == CTRMODE then
1093 return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
1094 else
1095 error("Unknown mode", 2)
1096 end
1097 end
1098 --
1099 -- Decrypts string data with password password.
1100 -- password - the decryption key is generated from this string
1101 -- data - string to encrypt
1102 -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1103 -- mode - mode of decryption: ecb, cbc(default), ofb, cfb
1104 --
1105 -- mode and keyLength must be the same for encryption and decryption.
1106 --
1107 function decrypt(password, data, keyLength, mode, iv)
1108 local mode = mode or CBCMODE
1109 local keyLength = keyLength or AES128
1110 local key = pwToKey(password, keyLength, iv)
1111 local plain
1112 if mode == ECBMODE then
1113 plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
1114 elseif mode == CBCMODE then
1115 plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
1116 elseif mode == OFBMODE then
1117 plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
1118 elseif mode == CFBMODE then
1119 plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
1120 elseif mode == CTRMODE then
1121 plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
1122 else
1123 error("Unknown mode", 2)
1124 end
1125 result = util.unpadByteString(plain)
1126 if (result == nil) then
1127 return nil
1128 end
1129 return result
1130 end
1131end
1132
1133-- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
1134-- By Anavrins
1135-- For help and details, you can PM me on the CC forums
1136-- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
1137-- Pastebin: http://pastebin.com/6UV4qfNF
1138-- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
1139local digest, hmac, pbkdf2
1140do
1141 local mod32 = 2^32
1142 local sha_hashlen = 32
1143 local sha_blocksize = 64
1144 local band = bit32 and bit32.band or bit.band
1145 local bnot = bit32 and bit32.bnot or bit.bnot
1146 local bxor = bit32 and bit32.bxor or bit.bxor
1147 local blshift = bit32 and bit32.lshift or bit.blshift
1148 local upack = unpack
1149
1150 local function rrotate(n, b)
1151 local s = n/(2^b)
1152 local f = s%1
1153 return (s-f) + f*mod32
1154 end
1155
1156 local function brshift(int, by) -- Thanks bit32 for bad rshift
1157 local s = int / (2^by)
1158 return s - s%1
1159 end
1160
1161 local H = {
1162 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
1163 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
1164 }
1165
1166 local K = {
1167 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1168 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1169 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1170 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1171 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1172 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1173 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1174 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
1175 }
1176
1177 local function counter(incr)
1178 local t1, t2 = 0, 0
1179 if 0xFFFFFFFF - t1 < incr then
1180 t2 = t2 + 1
1181 t1 = incr - (0xFFFFFFFF - t1) - 1
1182 else t1 = t1 + incr
1183 end
1184 return t2, t1
1185 end
1186
1187 local function BE_toInt(bs, i)
1188 return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
1189 end
1190
1191 local function preprocess(data)
1192 local len = #data
1193 local proc = {}
1194 data[#data+1] = 0x80
1195 while #data%64~=56 do data[#data+1] = 0 end
1196 local blocks = math.ceil(#data/64)
1197 for i = 1, blocks do
1198 proc[i] = {}
1199 for j = 1, 16 do
1200 proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
1201 end
1202 end
1203 proc[blocks][15], proc[blocks][16] = counter(len*8)
1204 return proc
1205 end
1206
1207 local function digestblock(w, C)
1208 for j = 17, 64 do
1209 local v = w[j-15]
1210 local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
1211 local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
1212 w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
1213 end
1214 local a, b, c, d, e, f, g, h = upack(C)
1215 for j = 1, 64 do
1216 local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
1217 local ch = bxor(band(e, f), band(bnot(e), g))
1218 local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
1219 local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
1220 local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
1221 local temp2 = (S0 + maj)%mod32
1222 h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
1223 end
1224 C[1] = (C[1] + a)%mod32
1225 C[2] = (C[2] + b)%mod32
1226 C[3] = (C[3] + c)%mod32
1227 C[4] = (C[4] + d)%mod32
1228 C[5] = (C[5] + e)%mod32
1229 C[6] = (C[6] + f)%mod32
1230 C[7] = (C[7] + g)%mod32
1231 C[8] = (C[8] + h)%mod32
1232 return C
1233 end
1234
1235 local mt = {
1236 __tostring = function(a) return string.char(unpack(a)) end,
1237 __index = {
1238 toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end,
1239 isEqual = function(self, t)
1240 if type(t) ~= "table" then return false end
1241 if #self ~= #t then return false end
1242 local ret = 0
1243 for i = 1, #self do
1244 ret = bit32.bor(ret, bxor(self[i], t[i]))
1245 end
1246 return ret == 0
1247 end
1248 }
1249 }
1250
1251 local function toBytes(t, n)
1252 local b = {}
1253 for i = 1, n do
1254 b[(i-1)*4+1] = band(brshift(band(t[i], 0xFF000000), 24), 0xFF)
1255 b[(i-1)*4+2] = band(brshift(band(t[i], 0xFF0000), 16), 0xFF)
1256 b[(i-1)*4+3] = band(brshift(band(t[i], 0xFF00), 8), 0xFF)
1257 b[(i-1)*4+4] = band(t[i], 0xFF)
1258 end
1259 return setmetatable(b, mt)
1260 end
1261
1262 digest = function(data)
1263 data = data or ""
1264 data = type(data) == "string" and {data:byte(1,-1)} or data
1265 data = preprocess(data)
1266 local C = {upack(H)}
1267 for i = 1, #data do C = digestblock(data[i], C) end
1268 return toBytes(C, 8)
1269 end
1270
1271 hmac = function(data, key)
1272 local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
1273 local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)}
1274 local blocksize = sha_blocksize
1275 key = #key > blocksize and digest(key) or key
1276 local ipad = {}
1277 local opad = {}
1278 local padded_key = {}
1279 for i = 1, blocksize do
1280 ipad[i] = bxor(0x36, key[i] or 0)
1281 opad[i] = bxor(0x5C, key[i] or 0)
1282 end
1283 for i = 1, #data do
1284 ipad[blocksize+i] = data[i]
1285 end
1286 ipad = digest(ipad)
1287 for i = 1, blocksize do
1288 padded_key[i] = opad[i]
1289 padded_key[blocksize+i] = ipad[i]
1290 end
1291 return digest(padded_key)
1292 end
1293
1294 pbkdf2 = function(pass, salt, iter, dklen)
1295 local out = {}
1296 local hashlen = sha_hashlen
1297 local block = 1
1298 dklen = dklen or 32
1299 while dklen > 0 do
1300 local ikey = {}
1301 local isalt = type(salt) == "table" and {upack(salt)} or {tostring(salt):byte(1,-1)}
1302 local clen = dklen > hashlen and hashlen or dklen
1303 local iCount = #isalt
1304 isalt[iCount+1] = band(brshift(band(block, 0xFF000000), 24), 0xFF)
1305 isalt[iCount+2] = band(brshift(band(block, 0xFF0000), 16), 0xFF)
1306 isalt[iCount+3] = band(brshift(band(block, 0xFF00), 8), 0xFF)
1307 isalt[iCount+4] = band(block, 0xFF)
1308 for j = 1, iter do
1309 isalt = hmac(isalt, pass)
1310 for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
1311 if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
1312 end
1313 dklen = dklen - clen
1314 block = block+1
1315 for k = 1, clen do out[#out+1] = ikey[k] end
1316 end
1317 return setmetatable(out, mt)
1318 end
1319end
1320
1321local function filterDeviceList()
1322 for i = #filteredDevices, 1, -1 do
1323 filteredDevices[i] = nil
1324 end
1325 local deviceCount = 0
1326 for i = 1, #allDevices do
1327 if (type(uFilter) == "boolean" and uFilter and allDevices[i].outputType:sub(1, 1) == "U") or (not uFilter) or (type(uFilter) == "string" and allDevices[i].outputType:sub(1, 1) ~= "U") then
1328 deviceCount = deviceCount + 1
1329 filteredDevices[deviceCount] = { }
1330 for k, v in pairs(allDevices[i]) do
1331 filteredDevices[deviceCount][k] = v
1332 end
1333 filteredDevices[deviceCount].adList = i
1334 end
1335 end
1336 numPages = math.max(1, math.ceil(#filteredDevices / 24))
1337 pageNum = math.min(pageNum, numPages)
1338end
1339
1340do
1341 local function sortDeviceList(newSettings)
1342 local deviceCount = #allDevices
1343 if deviceCount == 0 then --# the list is empty
1344 allDevices[1] = { }
1345 for k, v in pairs(newSettings) do --# add this device
1346 allDevices[1][k] = v
1347 end
1348 else
1349 local nsName, filterCount = newSettings.name, #filteredDevices
1350 for i = 1, deviceCount do
1351 if cc10host == allDevices[i].cc then --# we're already on the list
1352 for k, v in pairs(newSettings) do --# update entry with current state of host
1353 allDevices[i][k] = v
1354 end
1355 for j = 1, filterCount do
1356 if filteredDevices[j].adList == i then
1357 for k, v in pairs(newSettings) do
1358 filteredDevices[j][k] = v
1359 end
1360 break
1361 end
1362 end
1363 return
1364 end
1365 end
1366 for i = 1, deviceCount do
1367 if nsName < allDevices[i].name then --# alphabetize
1368 table.insert(allDevices, i, newSettings) --# insert
1369 break
1370 end
1371 if i == deviceCount then --# we've reached the end of the list
1372 deviceCount = deviceCount + 1
1373 allDevices[deviceCount] = { } --# tack it on
1374 for k, v in pairs(newSettings) do
1375 allDevices[deviceCount][k] = v
1376 end
1377 break
1378 end
1379 end
1380 end
1381 filterDeviceList()
1382 end
1383
1384 netReceive = function()
1385 local id, success, encKey, message, encryptedMessage, decryptedMessage, decodedMessage
1386 while true do
1387 if not rednet.isOpen(netSide) then rednet.open(netSide) end
1388 id, encryptedMessage = rednet.receive("cc10")
1389 if type(encryptedMessage) == "string" then
1390 success, decodedMessage = pcall(decode, encryptedMessage)
1391 if success and type(decodedMessage) == "string" then
1392 encKey = thisCC .. "cc!10" .. tostring(id)
1393 success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1394 if success and type(decryptedMessage) == "string" then
1395 success, message = pcall(textutils.unserialize, decryptedMessage)
1396 if success and type(message) == "table" and message.program and message.program == "cc10host" then
1397 cc10host = id
1398 sortDeviceList(message)
1399 drawCLI()
1400 end
1401 end
1402 end
1403 end
1404 end
1405 end
1406end
1407
1408local function netSend(targetHost, cmd1, act1)
1409 local dataPack = textutils.serialize({ program = "cc10", cmd = cmd1, action = act1 })
1410 if not rednet.isOpen(netSide) then rednet.open(netSide) end
1411 if targetHost == "ALL" then
1412 local encKey = thisCC .. "cc!10_Broadcast" .. thisCC
1413 local encryptedData = encode(encrypt(encKey, dataPack))
1414 rednet.broadcast(encryptedData, "cc10")
1415 else
1416 local encKey = tostring(targetHost) .. "cc!10" .. thisCC
1417 local encryptedData = encode(encrypt(encKey, dataPack))
1418 rednet.send(targetHost, encryptedData, "cc10")
1419 end
1420end
1421
1422local function assignColor(assignment)
1423 return colorBurst[assignment].color or silver
1424end
1425
1426local function drawElement(x, y, w, h, txtColor, bgColor, text)
1427 if type(w) == "boolean" then
1428 term.setCursorPos(x, y)
1429 term.setBackgroundColor(w and green or gray)
1430 term.write(" ")
1431 term.setBackgroundColor(w and gray or red)
1432 term.write(" ")
1433 else
1434 text = text and tostring(text) or ""
1435 local txtLen = #text
1436 w = math.max(w, txtLen)
1437 local spacer = (w - txtLen) / 2
1438 local txtLine = string.rep(" ", math.floor(spacer)) .. text .. string.rep(" ", math.ceil(spacer))
1439 if txtColor then term.setTextColor(txtColor) end
1440 if bgColor then term.setBackgroundColor(bgColor) end
1441 if h == 1 then
1442 term.setCursorPos(x, y)
1443 term.write(txtLine)
1444 else
1445 local line, textRow = string.rep(" ", w), y + math.floor(h / 2)
1446 for i = y, y + h - 1 do
1447 term.setCursorPos(x, i)
1448 term.write(i == textRow and txtLine or line) --# Draw one line of the 'element' (box/rectangle/line-seg)
1449 end
1450 end
1451 end
1452end
1453
1454local function drawHeader()
1455 local hColor = (runState == "Edit" or runState == "color") and assignColor(allDevices[thisDevice].color) or blue
1456 local hText = runState == "Help" and "cc10 " .. cc10Rver or ((runState == "Edit" or runState == "color") and allDevices[thisDevice].name .. " (cc# " .. tostring(allDevices[thisDevice].cc) .. ")" or "cc10")
1457 drawElement(1, 1, termX, 1, white, hColor, hText) --# title bar
1458 if runState == "List" or runState == "goPage" then
1459 drawElement(1, 1, 1, 1, nil, nil, "[ ]")
1460 local fColor = uFilter and green or gray
1461 drawElement(2, 1, 1, 1, type(uFilter) == "boolean" and fColor or orange, nil, "U")
1462 drawElement(termX - 2, 1, 1, 1, white, nil, "[ ]")
1463 drawElement(termX - 1, 1, 1, 1, red, nil, "X")
1464 drawElement(1, 2, 1, 1, green, gray, string.rep(" ", math.floor(termX / 4) - 2) .. "All ON" .. string.rep(" ", math.ceil(termX / 4) - 2))
1465 drawElement(math.ceil(termX / 2), 2, 1, 1, red, nil, string.rep(" ", math.floor(termX / 4) - 2) .. "All OFF" .. string.rep(" ", math.ceil(termX / 4) - 2))
1466 elseif runState == "Edit" or runState == "color" or runState == "Help" then
1467 drawElement(1, 2, termX, 1, red, gray, "Close")
1468 end
1469end
1470
1471local function drawColorList(rating)
1472 local color
1473 drawElement(8, 4, 9, 8, nil, gray) --# menu body
1474 for k, v in pairs(colorBurst) do
1475 if rating == k then
1476 color = v.color
1477 drawElement(16, v.order + 3, 1, 1, nil, color, " ") --# selected color pip
1478 else
1479 color = silver
1480 end
1481 drawElement(7, v.order + 3, 1, 1, nil, v.color, " ") --# rating color pips
1482 if rating == "I" and k == "I" then color = white end
1483 drawElement(9, v.order + 3, 1, 1, color, gray, v.text)
1484 end
1485end
1486
1487local function viewDeviceEntry()
1488 runState, outputType, onState = "Edit", allDevices[thisDevice].outputType, allDevices[thisDevice].onState
1489 drawHeader()
1490 if thisDevice <= #allDevices then
1491 drawElement(2, 4, 1, 1, assignColor(allDevices[thisDevice].color), black, allDevices[thisDevice].name) --# Name
1492 drawElement(12, 4, 1, 1, gray, nil, "Power:") --# Power state
1493 drawElement(22, 4, allDevices[thisDevice].state ~= 0)
1494 drawElement(2, 6, 1, 1, gray, black, "cc#") --# cc#
1495 drawElement(6, 6, 1, 1, white, nil, tostring(allDevices[thisDevice].cc))
1496 if outputType:sub(1, 1) ~= "P" then --# Restore state upon restart ON/OFF
1497 drawElement(12, 6, 1, 1, gray, nil, "Restore:")
1498 drawElement(22, 6, allDevices[thisDevice].restore)
1499 else
1500 drawElement(12, 6, 14, 1, nil, black)
1501 end
1502 drawElement(2, 8, 1, 1, silver, black, allDevices[thisDevice].note) --# Short description
1503 drawElement(2, 10, 1, 1, gray, nil, "Output type:") --# Output Type (Digital, Analog, Universal Digital, Universal Analog, Pulsed (short), Pulsed (long), Pulsed (3))
1504 drawElement(15, 10, 1, 1, silver, nil, (cc10Types[outputType][1] or "ERROR ") .. " ")
1505 if outputType == "A" or outputType == "UA" then --# Output Level (0-15)
1506 drawElement(2, 12, 1, 1, gray, nil, "Output Level:")
1507 local sState = onState < 10 and " " .. tostring(onState) or tostring(onState)
1508 drawElement(17, 12, 1, 1, white, nil, sState)
1509 drawElement(2, 13, 1, 1, silver, nil, "|---------------|")
1510 drawElement(onState + 2, 13, 1, 1, white, (onState > 0 and onState < 16) and green or red, "+")
1511 else
1512 drawElement(2, 12, 17, 2, nil, black)
1513 end
1514 if allDevices[thisDevice].loc.x ~= "No GPS Fix" then --# Device location (Export to pgps button)
1515 drawElement(14, 17, 9, 1, black, gray, "Export")
1516 drawElement(14, 18, 9, 1, nil, nil, "to pgps")
1517 end
1518 drawElement(2, 15, 1, 1, silver, black, "Dim:") --# Device location (dim/x/y/z)
1519 drawElement(2, 16, 1, 1, nil, nil, "x:")
1520 drawElement(2, 17, 1, 1, nil, nil, "y:")
1521 drawElement(2, 18, 1, 1, nil, nil, "z:")
1522 drawElement(7, 15, 1, 1, brown, nil, allDevices[thisDevice].loc.dim)
1523 drawElement(5, 16, 1, 1, sky, nil, tostring(allDevices[thisDevice].loc.x))
1524 drawElement(5, 17, 1, 1, nil, nil, tostring(allDevices[thisDevice].loc.y))
1525 drawElement(5, 18, 1, 1, nil, nil, tostring(allDevices[thisDevice].loc.z))
1526 end
1527end
1528
1529local function drawHelpUI()
1530 drawElement(1, 3, termX, termY - 2, nil, white)
1531 drawHeader()
1532 if tempState == "List" then
1533 drawElement(2, 4, 1, 1, black, white, "Left click an entry to")
1534 drawElement(2, 5, 1, 1, nil, nil, "toggle its state")
1535 drawElement(2, 7, 1, 1, nil, nil, "Right click an entry to")
1536 drawElement(2, 8, 1, 1, nil, nil, "view that entry's details")
1537 drawElement(2, 10, 1, 1, nil, nil, "Middle click an entry to")
1538 drawElement(2, 11, 1, 1, nil, nil, "lock it")
1539 drawElement(2, 13, 1, 1, nil, nil, "All ON/All OFF toggles")
1540 drawElement(2, 14, 1, 1, nil, nil, "all Universals in range")
1541 drawElement(2, 16, 1, 1, nil, nil, "[U] = Universal Filter")
1542 drawElement(2, 17, 1, 1, nil, nil, "Switch filter between")
1543 drawElement(2, 18, 1, 1, nil, nil, "universal, non-U, & all")
1544 elseif tempState == "Edit" then
1545 drawElement(2, 4, 1, 1, black, white, "Restore: restore last")
1546 drawElement(2, 5, 1, 1, nil, nil, "state after server or")
1547 drawElement(2, 6, 1, 1, nil, nil, "software restart")
1548 drawElement(2, 8, 1, 1, nil, nil, "Left click name, note, ")
1549 drawElement(2, 9, 1, 1, nil, nil, "or dim to edit")
1550 drawElement(2, 11, 1, 1, nil, nil, "Right click name to")
1551 drawElement(2, 12, 1, 1, nil, nil, "change color")
1552 drawElement(2, 14, 1, 1, nil, nil, "Universal Digital and")
1553 drawElement(2, 15, 1, 1, nil, nil, "Universal Analog both")
1554 drawElement(2, 16, 1, 1, nil, nil, "respond to All ON/OFF")
1555 end
1556end
1557
1558local function drawNaviUI()
1559 local pNum = tostring(pageNum)
1560 if pageNum < 100 then pNum = "0" .. pNum end --# Add a "0" before double digit page numbers
1561 if pageNum < 10 then pNum = "0" .. pNum end --# Add another "0" before single digit page numbers
1562 drawElement(math.floor(termX / 2) - 10, termY, 7, 1, gray, black, pageNum > 1 and "<< BACK" or " ")
1563 drawElement(math.floor(termX / 2) + 4, termY, 7, 1, nil, nil, pageNum < numPages and "NEXT >>" or " ")
1564 drawElement(math.floor((termX - #pNum) / 2) + 1, termY, 3, 1, silver, nil, pNum) --# Page Number
1565end
1566
1567local function drawMainUI() --# Device 'Address Book'
1568 local xPos, yPos, fdName, fdState, spacer = 1, 4
1569 local magicNumber = ((pageNum - 1) * 23) + pageNum
1570 for i = magicNumber, math.min(#filteredDevices, pageNum * 24) do
1571 term.setCursorPos(xPos, yPos)
1572 fdState = filteredDevices[i].state
1573 if not filteredDevices[i].lockState then
1574 term.setBackgroundColor(fdState == 0 and black or assignColor(filteredDevices[i].color))
1575 term.setTextColor(fdState == 0 and assignColor(filteredDevices[i].color) or white)
1576 else
1577 term.setBackgroundColor(fdState == 0 and black or gray)
1578 term.setTextColor(fdState == 0 and gray or silver)
1579 end
1580 fdName = filteredDevices[i].name
1581 spacer = (8 - #fdName) / 2
1582 term.write(string.rep(" ", math.floor(spacer)) .. fdName .. string.rep(" ", math.ceil(spacer)))
1583 yPos = yPos + 2
1584 if yPos > 19 then yPos = 4 xPos = xPos + 9 end
1585 end
1586end
1587
1588do
1589 local runStates = {
1590 List = function() drawMainUI() drawNaviUI() end;
1591 Edit = function() viewDeviceEntry() end;
1592 }
1593
1594 drawCLI = function() --# Client Interface 'decider'
1595 if not inputting then
1596 drawHeader()
1597 if runStates[runState] then runStates[runState]() end
1598 end
1599 end
1600end
1601
1602local function readInput(cX, cY, cO, bG, limit, mask) --# cursor X, Y, text color, bg color, character limit, character mask
1603 local word, pos = "", 0
1604 local curX, curY = cX, cY
1605 inputting, timerDelay = true, true
1606 os.cancelTimer(pollTimer)
1607 bG = bG or black
1608 limit = (limit and type(limit) == "number" and limit > 0) and limit or 21
1609 term.setCursorPos(curX, curY)
1610 term.setTextColor(cO)
1611 term.setBackgroundColor(bG)
1612 term.setCursorBlink(true)
1613 while true do
1614 local event, data, mX, mY = os.pullEvent()
1615 if event == "key" then
1616 if data == keys.backspace then
1617 if curX > cX then
1618 curX = math.max(cX, curX - 1)
1619 pos = math.max(0, pos - 1)
1620 word = word:sub(1, #word - 1)
1621 end
1622 drawElement(curX, curY, 1, 1, cO, bG, " ")
1623 term.setCursorPos(curX, curY)
1624 elseif data == keys.enter or data == keys.numPadEnter then
1625 break
1626 end
1627 elseif event == "char" and pos < limit then
1628 drawElement(curX, curY, 1, 1, cO, bG, mask or data)
1629 word = word .. data
1630 curX = curX + 1
1631 pos = pos + 1
1632 elseif event == "paste" and pos < limit then
1633 if pos + #data > limit then
1634 data = data:sub(1, limit - pos)
1635 end
1636 drawElement(curX, curY, 1, 1, cO, bG, mask and string.rep(mask, #data) or data)
1637 word = word .. data
1638 curX = curX + #data
1639 pos = pos + #data
1640 elseif event == "mouse_click" then
1641 if mY ~= curY or mX < cX or mX >= cX + limit then
1642 break
1643 end
1644 end
1645 end
1646 term.setCursorBlink(false)
1647 inputting = false
1648 return word
1649end
1650
1651do
1652 local function toggleFilter()
1653 if type(uFilter) == "boolean" then
1654 uFilter = uFilter and "U" or true
1655 else
1656 uFilter = false
1657 end
1658 filterDeviceList()
1659 drawElement(1, 3, termX, termY - 2, nil, black)
1660 drawCLI()
1661 end
1662
1663 local function inputMain()
1664 local event, mButton, mcX, mcY, xPos, yPos, magicNumber
1665 while true do
1666 event, mButton, mcX, mcY = os.pullEvent()
1667 if event == "mouse_click" then
1668 if runState == "Help" then
1669 if mcY == 2 and mButton == 1 then
1670 drawElement(1, 2, termX, termY - 1, nil, black)
1671 runState = tempState
1672 drawCLI()
1673 end
1674 elseif runState == "Edit" then
1675 if mcY == 2 and mButton == 1 then --# Exit Edit Screen
1676 drawElement(2, 2, termX, termY - 1, nil, black)
1677 runState = "List"
1678 drawCLI()
1679 elseif mcY == 4 then
1680 if mcX > 1 and mcX < 10 then
1681 if mButton == 1 then --# Edit Device Name
1682 runState = "EditName"
1683 return
1684 elseif mButton == 2 then --# Change color assignment
1685 drawColorList(allDevices[thisDevice].color)
1686 runState = "color"
1687 return
1688 end
1689 elseif mcX > 21 and mcX < 26 and mButton == 1 then --# Change ON/OFF state
1690 if allDevices[thisDevice].state == 0 then
1691 netSend(allDevices[thisDevice].cc, "set", allDevices[thisDevice].onState)
1692 elseif allDevices[thisDevice].state > 0 and allDevices[thisDevice].state < 16 then
1693 netSend(allDevices[thisDevice].cc, "set", 0)
1694 end
1695 end
1696 elseif mcY == 6 and mButton == 1 then
1697 if mcX > 21 and mcX < 26 and allDevices[thisDevice].outputType:sub(1, 1) ~= "P" then --# Restore state on restart?
1698 netSend(allDevices[thisDevice].cc, "restore", not allDevices[thisDevice].restore)
1699 end
1700 elseif mcY == 8 and mButton == 1 then --# Edit Device Note
1701 if mcX > 1 then
1702 runState = "EditNote"
1703 return
1704 end
1705 elseif mcY == 10 and mButton == 1 then --# Edit Output Type (Analog/Digital/Universal/Pulsed)
1706 if mcX > 14 and mcX < 24 then
1707 netSend(allDevices[thisDevice].cc, "changeOutputType", cc10Types[allDevices[thisDevice].outputType][2])
1708 end
1709 elseif mcY == 13 and mButton == 1 then --# Edit Redstone Output Level
1710 if mcX > 2 and mcX < 18 then
1711 if allDevices[thisDevice].outputType == "A" or allDevices[thisDevice].outputType == "UA" then
1712 netSend(allDevices[thisDevice].cc, "lvl", mcX - 2)
1713 end
1714 end
1715 elseif mcY == 15 and mButton == 1 then --# Device Dimension
1716 if mcX > 6 and mcX < 26 then
1717 runState = "EditDim"
1718 return
1719 end
1720 elseif (mcY == 17 or mcY == 18) and mButton == 1 then
1721 if mcX > 14 and mcX < 23 and allDevices[thisDevice].loc.x ~= "No GPS Fix" then --# Export to Lyqyd's pocketgps
1722 drawElement(14, 17, 9, 1, white, gray, "Export")
1723 drawElement(14, 18, 9, 1, nil, nil, "to pgps")
1724 sleep(0.1)
1725 drawElement(14, 17, 9, 1, black, nil, "Export")
1726 drawElement(14, 18, 9, 1, nil, nil, "to pgps")
1727 local pgpsPlaces = fs.open("/.places", fs.exists("/.places") and "a" or "w")
1728 pgpsPlaces.writeLine(allDevices[thisDevice].loc.x .. ", " .. allDevices[thisDevice].loc.y .. ", " .. allDevices[thisDevice].loc.z .. ", " .. allDevices[thisDevice].name)
1729 pgpsPlaces.close()
1730 end
1731 end
1732 elseif runState == "List" then
1733 if mcY == 1 and mButton == 1 then
1734 if mcX > 0 and mcX < 4 then --# Universal filter
1735 toggleFilter()
1736 elseif mcX > termX - 3 and mcX <= termX then --# Exit cc10 Remote
1737 kernelState = false
1738 if rednet.isOpen(netSide) then rednet.close(netSide) end
1739 term.setBackgroundColor(black)
1740 term.setTextColor(white)
1741 term.clear()
1742 term.setCursorPos(1, 1)
1743 break
1744 end
1745 elseif mcY == 2 and mButton == 1 then --# ALL ON/OFF
1746 netSend("ALL", "set", mcX < termX / 2 and "ON" or "OFF")
1747 elseif mcY > 3 and mcY < 19 then --# Switch a listed device on or off, or view it's info
1748 magicNumber, xPos, yPos = ((pageNum - 1) * 23) + pageNum, 1, 4
1749 for i = magicNumber, math.min(#filteredDevices, pageNum * 24) do
1750 if mcX >= xPos and mcX < xPos + 8 and mcY == yPos then
1751 if not filteredDevices[i].lockState then
1752 if mButton == 1 then
1753 netSend(filteredDevices[i].cc, "set", filteredDevices[i].state == 0 and filteredDevices[i].onState or 0)
1754 drawMainUI()
1755 elseif mButton == 2 then
1756 term.clear()
1757 thisDevice = filteredDevices[i].adList
1758 viewDeviceEntry()
1759 elseif mButton == 3 then
1760 thisDevice = filteredDevices[i].adList
1761 netSend(filteredDevices[i].cc, "lock")
1762 end
1763 else
1764 thisDevice = filteredDevices[i].adList
1765 runState = "unlock"
1766 return
1767 end
1768 end
1769 yPos = yPos + 2
1770 if yPos > 19 then yPos = 4 xPos = xPos + 9 end
1771 end
1772 elseif mcY == termY and numPages > 1 and mButton == 1 then --# Page Navigation via click
1773 if mcX < math.floor(termX / 2) - 3 or mcX > math.floor(termX / 2) + 3 then --# Page Back / Forward
1774 pageNum = mcX < math.floor(termX / 2) - 3 and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
1775 if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY - 3, nil, black) end
1776 drawMainUI()
1777 drawNaviUI()
1778 elseif mcX > math.floor(termX / 2) - 2 and mcX < math.floor(termX / 2) + 2 then --# Page Numbers (Go To Page dialogue)
1779 runState = "goPage"
1780 return
1781 end
1782 end
1783 end
1784 elseif event == "mouse_scroll" and runState == "List" then
1785 pageNum = mButton == 1 and math.min(pageNum + 1, numPages) or math.max(1, pageNum - 1)
1786 if pageNum == numPages and numPages > 1 then drawElement(2, 4, termX, termY - 3, nil, black) end
1787 drawMainUI()
1788 drawNaviUI()
1789 elseif event == "key" then
1790 if mButton == keys.u and runState == "List" then --# U
1791 if runState == "List" then toggleFilter() end
1792 elseif mButton == keys.f1 then
1793 if runState ~= "Help" then
1794 tempState = runState
1795 runState = "Help"
1796 drawHelpUI()
1797 else
1798 drawElement(1, 2, termX, termY - 1, nil, black)
1799 runState = tempState
1800 drawCLI()
1801 end
1802 end
1803 end
1804 end
1805 end
1806
1807 local function colorPicker()
1808 local choice = false
1809 local _, button, x, y = os.pullEvent("mouse_click")
1810 if x > 6 and x < 17 and y > 3 and y < 12 and button == 1 then
1811 for colorChoice = 4, 11 do
1812 if y == colorChoice then
1813 for k, v in pairs(colorBurst) do
1814 if (colorChoice - 3) == v.order then
1815 netSend(allDevices[thisDevice].cc, "color", k)
1816 choice = true
1817 break
1818 end
1819 end
1820 end
1821 if choice then break end
1822 end
1823 end
1824 drawElement(7, 4, 10, 8, nil, black)
1825 viewDeviceEntry()
1826 end
1827
1828 local function editEntry()
1829 if runState == "EditName" then
1830 drawElement(2, 4, 1, 1, gray, black, allDevices[thisDevice].name)
1831 local newName = readInput(2, 4, yellow, nil, 8)
1832 if newName ~= "" then
1833 netSend(allDevices[thisDevice].cc, "name", newName)
1834 drawElement(2, 4, 8, 1)
1835 else
1836 drawElement(2, 4, 1, 1, assignColor(allDevices[thisDevice].color), nil, allDevices[thisDevice].name)
1837 end
1838 elseif runState == "EditNote" then
1839 drawElement(2, 8, 1, 1, gray, black, allDevices[thisDevice].note)
1840 local newNote = readInput(2, 8, white, nil, 21)
1841 if newNote ~= "" then
1842 netSend(allDevices[thisDevice].cc, "note", newNote)
1843 drawElement(2, 8, 21, 1)
1844 else
1845 drawElement(2, 8, 1, 1, silver, nil, allDevices[thisDevice].note)
1846 end
1847 elseif runState == "EditDim" then
1848 drawElement(7, 15, 1, 1, gray, black, allDevices[thisDevice].loc.dim)
1849 local newDim = readInput(7, 15, sky, nil, 19)
1850 if newDim ~= "" then
1851 netSend(allDevices[thisDevice].cc, "locDim", newDim)
1852 drawElement(7, 15, 19, 1)
1853 else
1854 drawElement(7, 15, 1, 1, brown, nil, allDevices[thisDevice].loc.dim)
1855 end
1856 end
1857 runState = "Edit"
1858 end
1859
1860 local function enterPassword()
1861 drawElement(math.floor(termX / 2) - 7, 8, 15, 1, white, gray, " Password: ")
1862 drawElement(math.floor(termX / 2) - 7, 9, 15, 2)
1863 drawElement(math.floor(termX / 2) - 6, 9, 13, 1, nil, black) --# input area bg
1864 local pass = readInput(math.floor(termX / 2) - 6, 9, lime, nil, 13, "*")
1865 if pass ~= "" then
1866 local pw = table.concat(pbkdf2(pass, "cc!10", 15))
1867 netSend(allDevices[thisDevice].cc, "unlock", pw)
1868 end
1869 drawElement(math.floor(termX / 2) - 7, 8, 15, 3)
1870 runState = "List"
1871 drawMainUI()
1872 end
1873
1874 local function goToPage()
1875 drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 1, white, gray, "Page:")
1876 drawElement(math.floor(termX / 2) - 3, termY - 2, 7, 2)
1877 drawElement(math.floor(termX / 2) - 2, termY - 2, 5, 1, nil, black) --# input area bg
1878 local newPage = tonumber(readInput(math.floor(termX / 2) - 1, termY - 2, lime, nil, 3))
1879 pageNum = newPage or pageNum
1880 pageNum = math.max(1, math.min(pageNum, numPages))
1881 if pageNum == numPages and numPages > 1 then
1882 drawElement(1, 4, termX, termY - 3)
1883 else
1884 drawElement(math.floor(termX / 2) - 3, termY - 3, 7, 3)
1885 end
1886 runState = "List"
1887 drawMainUI()
1888 drawNaviUI()
1889 end
1890
1891 local runStates = {
1892 List = function() inputMain() end;
1893 Edit = function() inputMain() end;
1894 Help = function() inputMain() end;
1895 EditName = function() editEntry() end;
1896 EditNote = function() editEntry() end;
1897 EditDim = function() editEntry() end;
1898 unlock = function() enterPassword() end;
1899 goPage = function() goToPage() end;
1900 color = function() colorPicker() end;
1901 }
1902
1903 userInput = function()
1904 repeat
1905 if runStates[runState] then runStates[runState]() end
1906 if kernelState and timerDelay and not inputting then
1907 timerDelay = false
1908 pollTimer = os.startTimer(0.1)
1909 end
1910 until not kernelState
1911 end
1912end
1913
1914local function dataPoller()
1915 local removal, _, timerID = false
1916 while true do
1917 _, timerID = os.pullEvent("timer")
1918 if timerID == pollTimer then
1919 if runState == "List" then
1920 for i = #allDevices, 1, -1 do
1921 if allDevices[i].quietCount > 1 then
1922 table.remove(allDevices, i)
1923 removal = true
1924 else
1925 allDevices[i].quietCount = allDevices[i].quietCount + 1
1926 end
1927 end
1928 if removal then
1929 removal = false
1930 filterDeviceList()
1931 drawElement(1, 4, termX, termY - 3, nil, black)
1932 drawMainUI()
1933 drawNaviUI()
1934 end
1935 end
1936 if not timerDelay then
1937 netSend("ALL", "QRY", "cc10QRY")
1938 pollTimer = os.startTimer(3)
1939 end
1940 end
1941 end
1942end
1943
1944term.setBackgroundColor(black)
1945term.clear()
1946if not term.isColor() or not pocket then term.setCursorPos(1, 1) error("Advanced Wireless Pocket Computer Required", 0) end
1947drawElement(2, 2, 1, 1, white, nil, "Initializing . . .")
1948if not os.getComputerLabel() then os.setComputerLabel("PocketPC.ID#" .. thisCC) end
1949if peripheral.isPresent("back") and peripheral.getType("back") == "modem" and peripheral.call("back", "isWireless") then
1950 rednet.open("back")
1951 netSide = "back"
1952else
1953 term.clear()
1954 drawElement(2, 2, 1, 1, red, nil, "No wireless")
1955 drawElement(2, 3, 1, 1, nil, nil, "modem detected!")
1956 drawElement(2, 5, 1, 1, nil, nil, "cc10 REQUIRES")
1957 drawElement(2, 6, 1, 1, nil, nil, "a wireless modem.")
1958 term.setCursorPos(1, 9)
1959 return
1960end
1961runState = "List"
1962drawElement(2, 4, 1, 1, white, nil, "Searching for hosts . . .")
1963netSend("ALL", "QRY", "cc10QRY")
1964pollTimer = os.startTimer(3)
1965term.clear()
1966drawCLI()
1967parallel.waitForAny(userInput, netReceive, dataPoller)