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