├── .gitmodules ├── README.md ├── charts ├── README.md ├── charts.lua └── examples │ ├── hist-example-1.lua │ └── prbar-example-1.lua ├── eumon ├── README.md └── eumon.lua ├── ffp ├── README.md ├── complex.lua ├── converter.py ├── ffp.lua └── ffplayer.py ├── game-of-life ├── README.md └── game-of-life.lua ├── gist ├── README.md └── gist.lua ├── hpm ├── README.md └── hpm.lua ├── libaevent ├── README.md └── aevent.lua ├── libbigint ├── README.md └── bigint.lua ├── libcsv ├── README.md └── csv.lua ├── libder-decoder ├── README.md └── der-decoder.lua ├── libhttp ├── README.md └── http.lua ├── libsemver ├── README.md ├── semver.lua └── semver.moon ├── libtls ├── README.md └── tls.lua ├── libtls13 ├── .busted ├── README.md ├── example │ ├── client.cer │ ├── client.lua │ ├── priv.der │ └── pub.der ├── script │ ├── check-programs-cfg.sh │ ├── evaluate-chain.py │ ├── generate-div64by32-test-data.py │ ├── generate-ec-test-data.py │ ├── generate-mod-vec-vec-test-data.py │ ├── generate-modpow-test-data.py │ ├── generate-sha-hashes.py │ └── verify-ecdsa-mul.py ├── src │ ├── asn.lua │ ├── asn │ │ ├── bitstring.lua │ │ ├── encode.lua │ │ └── oid.lua │ ├── base64.lua │ ├── buffer.lua │ ├── crypto │ │ ├── cipher │ │ │ ├── aes.lua │ │ │ ├── chacha20-poly1305.lua │ │ │ └── mode │ │ │ │ └── gcm.lua │ │ ├── curve25519.lua │ │ ├── curve25519 │ │ │ └── constants.lua │ │ ├── hash │ │ │ └── sha2.lua │ │ ├── hkdf.lua │ │ ├── hmac.lua │ │ ├── montgomery.lua │ │ ├── rsa.lua │ │ └── secp384r1.lua │ ├── error.lua │ ├── group.lua │ ├── handshake.lua │ ├── init.lua │ ├── io.lua │ ├── oc.lua │ ├── oc │ │ ├── group.lua │ │ ├── io.lua │ │ ├── rng.lua │ │ └── sigalg.lua │ ├── record.lua │ ├── sigalg.lua │ ├── util.lua │ ├── util │ │ ├── buffer.lua │ │ └── map.lua │ ├── x509.lua │ └── x509 │ │ ├── attr.lua │ │ ├── ext.lua │ │ └── sigalg.lua ├── test │ ├── crypto │ │ ├── cipher │ │ │ ├── aes.lua │ │ │ ├── chacha20-poly1305.lua │ │ │ └── mode │ │ │ │ └── gcm.lua │ │ ├── curve25519.lua │ │ ├── hash │ │ │ └── sha2.lua │ │ ├── hkdf.lua │ │ ├── hmac.lua │ │ ├── montgomery.lua │ │ ├── rsa.lua │ │ └── secp384r1.lua │ ├── data │ │ ├── ca-bundle.pem │ │ ├── div64by32.txt │ │ ├── ec.json │ │ ├── lencr-org.pem │ │ ├── mod-vec-vec.txt │ │ ├── modpow.txt │ │ ├── sha256.txt │ │ ├── sha384.txt │ │ └── sha512.txt │ ├── test-util.lua │ └── x509.lua └── tls13 ├── lua-lockbox ├── LICENSE ├── README.md └── lockbox │ ├── cipher │ ├── aes128.lua │ ├── aes192.lua │ ├── aes256.lua │ ├── des.lua │ ├── des3.lua │ ├── mode │ │ ├── cbc.lua │ │ ├── cfb.lua │ │ ├── ctr.lua │ │ ├── ecb.lua │ │ ├── ige.lua │ │ ├── ofb.lua │ │ └── pcbc.lua │ ├── tea.lua │ └── xtea.lua │ ├── digest │ ├── md2.lua │ ├── md4.lua │ ├── md5.lua │ ├── ripemd128.lua │ ├── ripemd160.lua │ ├── sha1.lua │ ├── sha2_224.lua │ └── sha2_256.lua │ ├── init.lua │ ├── kdf │ └── pbkdf2.lua │ ├── mac │ └── hmac.lua │ ├── padding │ ├── ansix923.lua │ ├── isoiec7816.lua │ ├── pkcs7.lua │ └── zero.lua │ └── util │ ├── array.lua │ ├── base64.lua │ ├── bit.lua │ ├── queue.lua │ └── stream.lua ├── lumber ├── LICENSE ├── README.md └── lumber.lua ├── man ├── gist ├── nanomachines └── nn ├── net-flash ├── README.md ├── bios.lua └── net-flash.lua ├── nn ├── README.md └── nn.lua ├── opg-chat ├── README.md ├── chat-modules │ ├── admin.module │ ├── channels.module │ ├── chanserv.module │ ├── help.module │ ├── hide.module │ ├── input.module │ ├── network.module │ └── user.module ├── chat.json └── opg-chat.lua ├── particly ├── README.md ├── examples │ └── 2017.lua └── particly.lua ├── pipedream ├── README.md └── pipedream.lua ├── programs.cfg ├── railtank ├── README.md └── railtank.lua ├── smap ├── README.md ├── smap.lua └── smap │ ├── audio │ └── init.lua │ ├── init.lua │ ├── input │ ├── midi.input.lua │ └── nbs.input.lua │ └── output │ ├── beep.output.lua │ ├── inb.output.lua │ ├── pcspkr.output.lua │ └── sound.output.lua ├── sniff ├── README.md └── sniff.lua ├── stars ├── README.md └── stars.lua └── synth ├── README.md ├── lib ├── GUI.lua ├── advancedLua.lua ├── color.lua ├── doubleBuffering.lua └── image.lua └── synth.lua /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libtls13/third-party/json.lua"] 2 | path = libtls13/third-party/json.lua 3 | url = https://github.com/rxi/json.lua 4 | [submodule "libtls13/third-party/wycheproof"] 5 | path = libtls13/third-party/wycheproof 6 | url = https://github.com/google/wycheproof.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fingercomp's programs 2 | This is a place where hopefully brilliant ideas become programs. 3 | 4 | It's been a long time since I've really updated anything here. 5 | Many things changed, so some programs may no longer work correctly. 6 | If you spot a bug, please *do* open a issue. 7 | 8 | ## Programs 9 | Each package resides in its own directory. 10 | Look there for more information. 11 | 12 | * `libsemver` 13 | * A MoonScript port of a parser of semantic version strings originally written in Python. 14 | * `libaevent` 15 | * An advanced event library. 16 | * `libder-decoder` 17 | * Decodes data encoded using Distinguished Encoding Rules, a subset of ASN.1. For example, x.509 certificates. 18 | * `libcsv` 19 | * A CSV parser. 20 | * `charts` 21 | * Progress bars and histograms with extra precision. 22 | * `particly` 23 | * A really simple program that uses a Particle Card to "draw" bitmaps in the world. 24 | * `pipedream` 25 | * A simple graphical program. 26 | * `railtank` 27 | * A fancy tank monitoring program. 28 | * `sniff` 29 | * A network sniffer. 30 | * `stars` 31 | * A simple graphical program. 32 | * `eumon` 33 | * An EU storage monitor. 34 | * `ffp` 35 | * A PCM (or WAV if the provided converter is used) player. 36 | * `lumber` 37 | * A basic lumberjack program for a robot which uses an axe that can chop the whole tree, e.g., thaumcraft's axe of the stream. 38 | * `synth` 39 | * A powerful interface to the sound card. 40 | * `libtls` 41 | * An implementation of TLS 1.2. 42 | * `libtls13` 43 | * An implementation of TLS 1.3. 44 | * `libhttp` 45 | * An incomplete HTTP/HTTPS 1.1 library implemented on top of TCP sockets. May be useful in some awkward cases. 46 | 47 | ### Repackaged 48 | Programs I didn't write but had to repackage here as dependencies. 49 | 50 | * `lua-lockbox` 51 | * The most awesome pure Lua cryptography toolkit that I've ever found. 52 | * `libbigint` 53 | * Enables one to have *very* big integers, storing them in a metatable. Basic arithmetic operations (like abs, addition, division, etc.) are supported. 54 | 55 | ### Unmaintained 56 | Programs I have no interest in maintaining. 57 | 58 | * `nn` 59 | * Nanomachines control program. 60 | * `gist` 61 | * Gist downloader and uploader. 62 | * `game-of-life` 63 | * My implementation of Life. 64 | * `opg-chat` 65 | * IRC-like OpenPeripheral glasses chat. 66 | * `smap` 67 | * A *s*imple *M*inecraft *a*udio *p*layer. 68 | -------------------------------------------------------------------------------- /charts/README.md: -------------------------------------------------------------------------------- 1 | Available for downloading on the [Hel repository](https://hel.fomalhaut.me/#packages/charts). 2 | 3 | ### Contents 4 | * `Container` 5 | * `Histogram` 6 | * `ProgressBar` 7 | 8 | ### Usage 9 | #### `Container` 10 | Container contains a chart. It provides some common options like fg color, bg color, width, height, GPU to use, etc. 11 | 12 | ##### Attributes 13 | * `Container.gpu` is the proxy to GPU to use. 14 | * `Container.fg` is the default foreground color. 15 | * `Container.bg` is the default bakground color. 16 | * `Container.x` and `Container.y` define the top-left point of the container. 17 | * `Container.payloadX` and `Container.payloadY` define the top-left point of the payload output area (relative to the container). 18 | * `Container.width` and `Container.height` define the dimension of the payload output area. 19 | * `Container.payload` is the chosen chart object. 20 | 21 | ##### Methods 22 | * `Container:draw()` draws the chart. Restores the colors when it's done. 23 | 24 | #### `Histogram` 25 | You can learn what histogram is [here](https://en.wikipedia.org/wiki/Histogram). 26 | 27 | The width of a histogram bar is 1 symbol. 28 | 29 | ##### Attributes 30 | * `Histogram.values` is the table of numeric values. 31 | * `Histogram.align` defines the alignment of the chart. 32 | * `Histogram.colorFunc` is the function that returns fg and bg colors. If one of the values is missing, the container's values will be used instead. The function is called with the following arguments: 33 | * Item index 34 | * Normalized value (0 to 1). 35 | * The value itself. 36 | * The histogram object. 37 | * The container object. 38 | * `Histogram.min` defines the value at the bottom. All histogram values must be greater than (or equal to) this value. 39 | * `Histogram.max` defines the value at the top. All histogram values must be less than (or equal to) this value. 40 | * `Histogram.level.y` sets the y coordinate of the histogram level (0 is default). 41 | * `Histogram.level.value` sets the level value. Values under the level value will be drawn under the level, and vice versa. 42 | 43 | #### `ProgressBar` 44 | Progress bar, uh, is a bar that displays the progress. Well, you've probably already seen progress bars before so you don't need an explanation, right? 45 | 46 | #### Attributes 47 | * `ProgressBar.value` is a numeric value. 48 | * `ProgressBar.max` is the maximum value of the progress bar. 49 | * `ProgressBar.min` is the minumum value of the progress bar. 50 | * `ProgressBar.direction` is the **direction** of the progress bar. This is where the end of the bar is. 51 | * `ProgressBar.colorFunc` is a function that returns fg and bg colors. If one of the values is missing, the container's values will be used instead. The function is called with the following arguments: 52 | * Value. 53 | * Normalized value (0 to 1). 54 | * Progress bar object. 55 | * Container object. 56 | 57 | ### Sample code 58 | ```lua 59 | local charts = require("charts") 60 | 61 | local container = charts.Container() 62 | local payload = charts.Histogram { 63 | max = 80, 64 | align = charts.sides.RIGHT, 65 | colorFunc = function(index, norm, value, self, container) 66 | return 0x20ff20 67 | end 68 | } 69 | container.payload = payload 70 | 71 | for i = 1, 400, 1 do 72 | table.insert(payload.values, math.random(0, 80)) 73 | container:draw() 74 | 75 | os.sleep(.05) 76 | end 77 | ``` 78 | 79 | ```lua 80 | local charts = require("charts") 81 | local term = require("term") 82 | local event = require("event") 83 | 84 | local cleft = charts.Container { 85 | x = 1, 86 | y = 1, 87 | width = 50, 88 | height = 2, 89 | payload = charts.ProgressBar { 90 | direction = charts.sides.LEFT, 91 | value = 0, 92 | colorFunc = function(_, perc) 93 | if perc >= .9 then 94 | return 0x20afff 95 | elseif perc >= .75 then 96 | return 0x20ff20 97 | elseif perc >= .5 then 98 | return 0xafff20 99 | elseif perc >= .25 then 100 | return 0xffff20 101 | elseif perc >= .1 then 102 | return 0xffaf20 103 | else 104 | return 0xff2020 105 | end 106 | end 107 | } 108 | } 109 | 110 | local cright = charts.Container { 111 | x = 1, 112 | y = 4, 113 | width = 50, 114 | height = 2, 115 | payload = charts.ProgressBar { 116 | direction = charts.sides.RIGHT, 117 | value = 0, 118 | colorFunc = cleft.payload.colorFunc 119 | } 120 | } 121 | 122 | local ctop = charts.Container { 123 | x = 55, 124 | y = 1, 125 | width = 2, 126 | height = 20, 127 | payload = charts.ProgressBar { 128 | direction = charts.sides.TOP, 129 | value = 0, 130 | colorFunc = cleft.payload.colorFunc 131 | } 132 | } 133 | 134 | local cbottom = charts.Container { 135 | x = 59, 136 | y = 1, 137 | width = 2, 138 | height = 20, 139 | payload = charts.ProgressBar { 140 | direction = charts.sides.BOTTOM, 141 | value = 0, 142 | colorFunc = cleft.payload.colorFunc 143 | } 144 | } 145 | 146 | for i = 0, 100, 1 do 147 | term.clear() 148 | cleft.gpu.set(5, 10, "Value: " .. ("%.2f"):format(i / 100) .. " [" .. ("%3d"):format(i) .. "%]") 149 | cleft.gpu.set(5, 11, "Max: " .. cleft.payload.min) 150 | cleft.gpu.set(5, 12, "Min: " .. cleft.payload.max) 151 | 152 | cleft.payload.value, cright.payload.value, ctop.payload.value, cbottom.payload.value = i / 100, i / 100, i / 100, i / 100 153 | 154 | cleft:draw() 155 | ctop:draw() 156 | cright:draw() 157 | cbottom:draw() 158 | 159 | if event.pull(0.05, "interrupted") then 160 | term.clear() 161 | os.exit() 162 | end 163 | end 164 | 165 | event.pull("interrupted") 166 | term.clear() 167 | ``` 168 | -------------------------------------------------------------------------------- /charts/examples/hist-example-1.lua: -------------------------------------------------------------------------------- 1 | local charts = require("charts") 2 | local event = require("event") 3 | local gpu = require("component").gpu 4 | 5 | local w, h = gpu.getViewport() 6 | 7 | local container = charts.Container { 8 | width = w, 9 | height = h, 10 | payload = charts.Histogram { 11 | max = 1, 12 | min = -1, 13 | level = { 14 | value = 0, 15 | y = .5 16 | }, 17 | align = charts.sides.RIGHT, 18 | colorFunc = function(index, perc, value, self, container) 19 | return value >= 0 and 0xafff20 or 0x20afff 20 | end 21 | } 22 | } 23 | 24 | for i = 1, math.huge, 1 do 25 | table.insert(container.payload.values, math.sin(math.rad(i * 3))) 26 | if #container.payload.values > container.width then 27 | table.remove(payload.values, 1) 28 | end 29 | container:draw() 30 | 31 | if event.pull(.05, "interrupted") then 32 | break 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /charts/examples/prbar-example-1.lua: -------------------------------------------------------------------------------- 1 | local charts = require("charts") 2 | local term = require("term") 3 | local event = require("event") 4 | 5 | local cleft = charts.Container { 6 | x = 1, 7 | y = 1, 8 | width = 50, 9 | height = 2, 10 | payload = charts.ProgressBar { 11 | direction = charts.sides.LEFT, 12 | value = 0, 13 | colorFunc = function(_, perc) 14 | if perc >= .9 then 15 | return 0x20afff 16 | elseif perc >= .75 then 17 | return 0x20ff20 18 | elseif perc >= .5 then 19 | return 0xafff20 20 | elseif perc >= .25 then 21 | return 0xffff20 22 | elseif perc >= .1 then 23 | return 0xffaf20 24 | else 25 | return 0xff2020 26 | end 27 | end 28 | } 29 | } 30 | 31 | local cright = charts.Container { 32 | x = 1, 33 | y = 4, 34 | width = 50, 35 | height = 2, 36 | payload = charts.ProgressBar { 37 | direction = charts.sides.RIGHT, 38 | value = 0, 39 | colorFunc = cleft.payload.colorFunc 40 | } 41 | } 42 | 43 | local ctop = charts.Container { 44 | x = 55, 45 | y = 1, 46 | width = 2, 47 | height = 20, 48 | payload = charts.ProgressBar { 49 | direction = charts.sides.TOP, 50 | value = 0, 51 | colorFunc = cleft.payload.colorFunc 52 | } 53 | } 54 | 55 | local cbottom = charts.Container { 56 | x = 59, 57 | y = 1, 58 | width = 2, 59 | height = 20, 60 | payload = charts.ProgressBar { 61 | direction = charts.sides.BOTTOM, 62 | value = 0, 63 | colorFunc = cleft.payload.colorFunc 64 | } 65 | } 66 | 67 | for i = 0, 100, 1 do 68 | term.clear() 69 | cleft.gpu.set(5, 10, "Value: " .. ("%.2f"):format(i / 100) .. " [" .. ("%3d"):format(i) .. "%]") 70 | cleft.gpu.set(5, 11, "Max: " .. cleft.payload.min) 71 | cleft.gpu.set(5, 12, "Min: " .. cleft.payload.max) 72 | 73 | cleft.payload.value, cright.payload.value, ctop.payload.value, cbottom.payload.value = i / 100, i / 100, i / 100, i / 100 74 | 75 | cleft:draw() 76 | ctop:draw() 77 | cright:draw() 78 | cbottom:draw() 79 | 80 | if event.pull(0.05, "interrupted") then 81 | term.clear() 82 | os.exit() 83 | end 84 | end 85 | 86 | event.pull("interrupted") 87 | term.clear() 88 | -------------------------------------------------------------------------------- /eumon/README.md: -------------------------------------------------------------------------------- 1 | # eumon 2 | *A simple but beautiful EU storage monitor.* 3 | 4 | ![Image](https://i.imgur.com/7byd0N3.png) 5 | -------------------------------------------------------------------------------- /ffp/README.md: -------------------------------------------------------------------------------- 1 | # ffp 2 | *An audio player* 3 | 4 | Plays PCM files (the provided converter converts WAV files, too). 5 | 6 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/ffp). 7 | 8 | ## Usage 9 | ### Playing PCM files directly 10 | ``` 11 | ffp $path $depth $sample_rate $channels $window_size $window_step $duration 12 | ``` 13 | 14 | * `$path` - path to a PCM file 15 | * `$depth` - bit depth 16 | * `$sample_rate` - sample rate 17 | * `$channels` - amount of sound card channels to use (8 by default) 18 | * `$window_size` - window size, in samples (1024 by default) 19 | * `$window_step` - does something, probably (1 by default) 20 | * `$duration` - how much to play, in seconds (by default the whole file) 21 | 22 | ### Converting a PCM file 23 | Twice as faster than playing directly. 24 | 25 | Run the script not on an OpenComputers machine. The usage remains the same, 26 | just redirect the output to a file. 27 | 28 | You can play the output file using the following command. 29 | 30 | ``` 31 | ffp --load $path $duration 32 | ``` 33 | 34 | * `$path` - path to the output file 35 | * `$duration` - how much to play, in seconds (the whole file if empty) 36 | 37 | ### Converting a WAV file 38 | This is the fastest option that uses numpy's FFT implementation to convert WAV 39 | files. The same audio file took more than 20 minutes to convert using the 40 | previous converter, and a minute using the converter written in Python. 41 | 42 | Requires Python 3, NumPy, SciPy. 43 | 44 | ``` 45 | ./converter.py $path $window_size $channels > out.smp 46 | ``` 47 | 48 | * `$path` - path to a WAV file 49 | * `$window_size` - size of window (1024 by default) 50 | * `$channels` - amount of channels to use (8 by default) 51 | 52 | ### Playing converted files 53 | A kind person (@kebufu) has provided us with an fft audio player in Python which 54 | means you can enjoy the hilariously high-quality audio without even having to 55 | launch the game! 56 | 57 | See 58 | 59 | ``` 60 | python3 ./ffplayer.py 61 | ``` 62 | 63 | for the usage help. 64 | Besides Python 3 (obviously), it seems to need `pyaudio` installed — 65 | so make sure it is. 66 | 67 | ### Requirements 68 | * Audio file: must be mono. 69 | * The OpenComputers program itself requires Lua 5.3, the sound card, and quite 70 | a lot of RAM. 71 | -------------------------------------------------------------------------------- /ffp/converter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import struct 5 | import sys 6 | 7 | import numpy as np 8 | import scipy.io.wavfile as wav 9 | 10 | stdout = os.fdopen(sys.stdout.fileno(), "wb") 11 | 12 | if len(sys.argv) == 1 or not sys.argv[1]: 13 | sys.stderr.write("Usage: %s [window size] [channels]\n" % sys.argv[0]) 14 | sys.exit(1) 15 | 16 | window_size = 1024 17 | channels = 8 18 | 19 | if len(sys.argv) >= 3: 20 | window_size = int(sys.argv[2]) 21 | if len(sys.argv) >= 4: 22 | channels = int(sys.argv[3]) 23 | 24 | sample_rate, data = wav.read(sys.argv[1]) 25 | 26 | if type(data[0]) == np.ndarray: 27 | sys.stderr.write("Mono audio required\n") 28 | sys.exit(1) 29 | 30 | stdout.write(struct.pack('>dddd', sample_rate, window_size, window_size, channels)) 31 | 32 | for i in range(0, len(data), window_size): 33 | window = data[i:i+window_size] 34 | n = len(window) 35 | freq = np.fft.fftfreq(n, 1/sample_rate)[range(int(n / 2))] 36 | fourier = np.fft.fft(window) / n 37 | fourier = fourier[range(int(n / 2))] 38 | values = [x for x in sorted(zip(freq, np.abs(fourier)), 39 | key=lambda x: x[1], 40 | reverse=True)] 41 | for ch in range(channels): 42 | stdout.write(struct.pack('>dd', values[ch][0], values[ch][1])) 43 | -------------------------------------------------------------------------------- /ffp/ffplayer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from os import path 3 | from math import floor 4 | from struct import unpack 5 | from pyaudio import PyAudio 6 | from sys import argv, stdout, stderr, exit 7 | 8 | if len(argv) == 1: 9 | print(f"{argv[0]} [-o outfile]", file=stderr) 10 | exit(-1) 11 | 12 | f = open(argv[1], "rb") 13 | 14 | 15 | def sin_wave(A, f, fs, phi, t): 16 | """ 17 | :params A: amplitude 18 | :params f: wave frequency 19 | :params fs: sample rate 20 | :params phi: phase 21 | :params t: time 22 | """ 23 | # An example for illustratory purposes: 24 | # If we let the time t = 1 s and the sample rate fs = 1000 Hz, 25 | # we obtain that the sampling interval equals Ts = 1 / fs = 0.001 s. 26 | # Therefore, the number of sample points is n = t / Ts = 1000; 27 | # that is, t seconds of sine wave is represented with n = t / Ts samples, 28 | # taken every Ts seconds. 29 | Ts = 1 / fs 30 | n = t / Ts 31 | n = np.arange(n) 32 | y = A * np.sin(2 * np.pi * f * n * Ts + phi * (np.pi / 180)) 33 | return y 34 | 35 | 36 | rate, windowSize, step, channels = unpack(">dddd", f.read(32)) 37 | fileSize = path.getsize(argv[1]) - 32 38 | length = (fileSize) / rate / channels / 8 / 2 * windowSize 39 | fileSize = floor(min(fileSize, length * rate * channels * 8 * 2 / windowSize)) 40 | 41 | delay = step / rate 42 | chans = [unpack(">d", f.read(8))[0] for i in range(0, fileSize, 8)] 43 | buffer = np.array([]) 44 | maxAmplitude = np.max(chans[1::2]) 45 | f.close() 46 | 47 | print(f"length: {fileSize}B/{length}s.") 48 | 49 | p = PyAudio() 50 | stream = p.open( 51 | format=p.get_format_from_width(2), 52 | channels=1, 53 | rate=int(rate), 54 | output=True, 55 | frames_per_buffer=int(step), 56 | ) 57 | 58 | try: 59 | out = argv.index("-o") 60 | import wave 61 | 62 | out = wave.open(argv[out + 1], "wb") 63 | out.setnchannels(1) 64 | out.setsampwidth(2) 65 | out.setframerate(rate) 66 | except: 67 | out = None 68 | 69 | for sample in range(0, len(chans), int(channels) * 2): 70 | buf = sin_wave( 71 | chans[sample + 1] / maxAmplitude / channels, chans[sample], rate, 0, delay 72 | ) 73 | 74 | for i in range(2, int(channels) * 2, 2): 75 | buf += sin_wave( 76 | chans[sample + 1 + i] / maxAmplitude / channels, 77 | chans[sample + i], 78 | rate, 79 | 0, 80 | delay, 81 | ) 82 | 83 | data = (buf * 32767).astype(np.int16).tobytes() 84 | stream.write(data) 85 | 86 | if out: 87 | out.writeframes(data) 88 | 89 | stdout.write( 90 | "\rPlaying: %0.2f/%0.2fs" % ((sample / channels / 2 + 1) * delay, length) 91 | ) 92 | 93 | stream.close() 94 | 95 | if out: 96 | out.close() 97 | -------------------------------------------------------------------------------- /game-of-life/README.md: -------------------------------------------------------------------------------- 1 | # Conway's Game of Life implementation for OpenComputers 2 | ![Screenshot of the game.](http://i.imgur.com/J7enOnc.png) 3 | 4 | * With the simulation paused, use the controls (descibed below) to place or kill cells. 5 | * Press `[Space]` and observe the magic evolution of these cells. 6 | 7 | ## Rules 8 | * The board updates at specific rate. 9 | * Every update the new generation is displayed. 10 | * The board is a toroidal array (the borders are "glued"). 11 | * The board is filled with cells. 12 | * Cells may be either *dead* or *alive*. 13 | * Every cell may have up to 8 neighbors. 14 | * Any live cell with **0-1 neighbors** *dies* (becomes a dead one). 15 | * Any live cell with **2 neighbors** *stays alive* in next generation. 16 | * Any live cell with more than **3 neighbors** *dies*. 17 | * **_Any_** cell with **exactly 3 neighbors** *becomes a live one* (if it was dead) and *stays alive* in the next generation. 18 | 19 | ### Highlighting 20 | * If cell will *die* on the next generation, it'll be red. 21 | * If cell will *be born* on the next generation, it'll be dark green. 22 | * If cell is *alive* on current and next generations, it will be white. 23 | 24 | ## Controls 25 | * `[Space]` *Start* or *pause* simulation. 26 | * `[q]` *Quit* the game. 27 | * `[Left Mouse Button]` *Place* a cell (only works if **the simulation is paused**). 28 | * `[Right Mouse Button]` *Kill* a cell (only works if **the simulation is paused**). 29 | * `[Enter]` *Next* generation (only works if **the simulation is paused**). 30 | * `[<]` *Descrease* the speed of the simulation. 31 | * `[>]` *Increase* the speed of the simulation. 32 | * `[c]` Toggle the *highlighting*. 33 | * `[Backspace]` *Clear* the board (only works if **the simulation is paused**). 34 | 35 | ## Requirements 36 | * T3 screen. 37 | * T3 graphics card. 38 | 39 | ## License 40 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 41 | -------------------------------------------------------------------------------- /gist/README.md: -------------------------------------------------------------------------------- 1 | # gist 2 | *Download and upload files from/to Gist* 3 | 4 | ## Synopsis 5 | `gist [-p] [--P=mode] [--d=desc] [-sRqQlriG] [--f=filename] [file]` 6 | 7 | ## Description 8 | This program makes it easy to download and upload files to/from Gist. 9 | 10 | ## Requirements 11 | * Internet card (HTTP requests have to be enabled in mod's configuration). 12 | 13 | ## Options 14 | If uploading, provide file list as arguments with the following format: `=` 15 | 16 | * `-p` 17 | * Upload files to gist. 18 | * `--P=mode` or `--public=mode` 19 | * **Upload**: `s` mode makes gist to be secret, `p` - public. 20 | * `--d=description` or `--description=description` 21 | * **Upload**: Set a description for a gist. 22 | * `-s` 23 | * If uploading, shorten the resulting URL. Else shorten the Github URL given as the first argument. 24 | * `-R` 25 | * Show URL to a raw file contents. 26 | * `-q` 27 | * Quiet mode. 28 | * `-Q` 29 | * Superquiet mode: do not show errors. 30 | * `-l` 31 | * List files in gist and quit. 32 | * `-r` 33 | * Override the file even if it exists. 34 | * `-i` 35 | * Show the file information. 36 | * `-G` 37 | * Show the gist information. 38 | * `--f=filename` or `--file=filename` 39 | * Specify the file to work with. 40 | * `-t` 41 | * Prompt for a GitHub OAuth [personal access token](https://github.com/settings/tokens) (needs to have *gist* scope to work). **Requires OpenComputers 1.6 and higher**. 42 | * `--t=token` or `--token=token` 43 | * Use the given token. *Not recommended*, since the token will be visible as a plain text. **Requires OpenComputers 1.6 and higher**. 44 | * `--u=gistid` 45 | * **Upload mode**. Instead of posting a new gist, update the existing one with the given ID. Provide the files to modify as if you were uploading to Gist. *Requires the correct token*, see `-t`. Also **requires OpenComputers 1.6 and higher**. 46 | 47 | ## Examples 48 | * `gist -s -p --P=s --d="Hello, world!" /examples/test.lua=test.lua` 49 | * Uploads file /example/test.lua to a secret gist with description "Hello, world" and shows the short URL. 50 | * `gist -G https://git.io/example` 51 | * Gets the gist information. 52 | * `gist 1a3b5b7c9d1e3f5a7b9c` 53 | * Prints gist's file contents. 54 | * `gist --f=helloworld.c -r https://git.io/example helloworlds/hello.c` 55 | * Saves the contents of the file helloworld.c in the gist to helloworlds/hello.c, overrides the file if it exists. 56 | * `gist -t --u=12345678901234567890 -p --d="New description" /new/file=file.lua` 57 | * Changes the description of the gist with the ID `12345678901234567890` to `New description` and updates contents of the file `file.lua`. Prompts for the GitHub OAuth [personal access token](https://github.com/settings/tokens). 58 | * `gist -s -p --d="Hello, world!" --P=s -t /hello/hello.cpp=hello.cpp` 59 | * Prompts for the GitHub OAuth [personal access token](https://github.com/settings/tokens) and then uploads the file `hello.cpp` to Gist. Returns a short URL to the gist. 60 | 61 | ## License 62 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 63 | -------------------------------------------------------------------------------- /hpm/README.md: -------------------------------------------------------------------------------- 1 | Installer of hpm installer. Run `hpm` to install an installer that installs 2 | `hpm`. Right. 3 | 4 | More info about hpm is in the main README. 5 | -------------------------------------------------------------------------------- /hpm/hpm.lua: -------------------------------------------------------------------------------- 1 | if not require("component").isAvailable("internet") then 2 | io.stderr:write("This program requires internet card.\n") 3 | return 1 4 | end 5 | 6 | print("Installing hpm...") 7 | os.execute("pastebin run vf6upeAN") 8 | print("Done.") 9 | -------------------------------------------------------------------------------- /libaevent/README.md: -------------------------------------------------------------------------------- 1 | # Libaevent 2 | *An advanced event library* 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/libaevent). 5 | 6 | The library was written by @LeshaInc. I only packaged it here so it's now available to be downloaded with OPPM, and wrote the documentation. 7 | 8 | ## Advantages 9 | Advantages over the standard event library: 10 | * It features the thing I really needed: pushing event with payload of **any** types, including functions and tables. 11 | * Priorities system. 12 | * The event system is *local*, which means you can have multiple event engines running simulateously, and the events *won't* interfere with other engines. 13 | * Events are classes. 14 | * Events are *cancellable*, cancelling one makes it not to go any further. 15 | 16 | ## Using the library 17 | The library returns an `Engine` *class*, which has to be initialized. Generally you'll need to do something like this (note the parentheses at the end): 18 | 19 | ```lua 20 | local engine = require("aevent")() 21 | ``` 22 | 23 | ### Methods 24 | 25 | #### The resulting `EvEngine` instance 26 | 27 | | Method | Description | 28 | | ------ | ----------- | 29 | | `engine:event(name: string)` | Registers an event with a name `name`. It may be then called to get an instance of the event. | 30 | | `engine:push(event: table(Event))` | Pushes the event instance. | 31 | | `engine:subscribe(name: string, priority: number, handler: function)` | Assigns a callable (function or table with defined `__call`) to an event with a specific priority. | 32 | | `engine:stdEvent(eventName: string, event: table(Event))` | Registers a new listener that will listen for the given event, and will fire instance of the given event. | 33 | | `engine:timer(interval: number, event: table(Event)[, times: number])` | Creates a new timer that will push the given event on every tick. | 34 | | `engine:__gc()` | Unregisters all listeners for the computer signals. Call this if the `__gc` metamethod is disabled. | 35 | 36 | #### The `Event` instance 37 | 38 | **Constructor:** 39 | - `Event(data: table[, once: bool])` Returns an instance of the event with the specified payload. The `once` arguments makes the event be processed by only one subscriber. Generally you'd use `Event {test = "test"}`. 40 | 41 | | Method | Description | 42 | | ---------------- | ---------------------------- | 43 | | `event:cancel()` | Cancels an event. | 44 | | `event:get()` | Returns the event's payload. | 45 | 46 | Event payload can be indexed just like tables, e.g. `event.foo`. 47 | 48 | ### Sample code 49 | ```lua 50 | local EvtEngine = require("aevent")() 51 | 52 | local Event1 = EvtEngine:event("event1") 53 | 54 | EvtEngine:subscribe("event1", 0, function(evt) 55 | print("0 callback!", evt.test) -- If the event is indexed, and the index is none of 56 | -- standard keys (e.g., cancel), it'll look for the 57 | -- value in the event's payload. 58 | evt:cancel() 59 | end) 60 | 61 | EvtEngine:subscribe("event1", 1, function(evt) 62 | print("1 callback!", evt.test) 63 | end) 64 | 65 | EvtEngine:push(Event1{ 66 | test = "Hello!" 67 | }) 68 | 69 | --> 0 callback! Hello! 70 | ``` 71 | -------------------------------------------------------------------------------- /libaevent/aevent.lua: -------------------------------------------------------------------------------- 1 | local _pairs, _ipairs = pairs, ipairs 2 | -- redefine to take a shadow copy of tables to avoid 3 | -- changes to tables while iterating 4 | local function pairs(tbl, ...) 5 | local copy = {} 6 | for k, v in _pairs(tbl) do 7 | copy[k] = v 8 | end 9 | return _pairs(copy, ...) 10 | end 11 | 12 | local function ipairs(tbl, ...) 13 | local copy = {} 14 | for k, v in _ipairs(tbl) do 15 | copy[k] = v 16 | end 17 | return _ipairs(copy, ...) 18 | end 19 | 20 | 21 | local meta = { 22 | __call = function (cls, incStdEvt) 23 | local self = setmetatable({}, {__index = cls}) 24 | 25 | self.keys = {} 26 | self.priorities = {} 27 | self.maxPriority = 0 28 | 29 | -- Create a new listener for each engine 30 | self.eventListener = function(e) 31 | return function(evt, ...) 32 | self:push(e({...})) 33 | end 34 | end 35 | 36 | return self 37 | end, 38 | __gc = function (self) 39 | if self.stdEvents then 40 | local event = require("event") 41 | for name, v in pairs(self.stdEvents) do 42 | for _, listener in pairs(v) do 43 | listener:destroy() 44 | end 45 | end 46 | end 47 | 48 | if self.timers then 49 | local event = require("event") 50 | for _, timer in pairs(self.timers) do 51 | timer:destroy() 52 | end 53 | end 54 | end 55 | } 56 | meta.__index = meta 57 | 58 | return setmetatable({ 59 | push = function (self, event) 60 | for _, i in pairs(self.keys) do 61 | local priority = self.priorities[i] 62 | 63 | if priority then 64 | for _, handler in pairs(priority) do 65 | if handler.targets[event.name] then 66 | handler(event) 67 | 68 | if event.canceled or event.once then 69 | return 70 | end 71 | end 72 | end 73 | end 74 | end 75 | end, 76 | 77 | subscribe = function (self, name, id, handler) 78 | if id > self.maxPriority then 79 | self.maxPriority = id 80 | end 81 | 82 | local found = false 83 | for _, key in ipairs(self.keys) do 84 | if key == id then 85 | found = true 86 | end 87 | end 88 | 89 | if not found then 90 | table.insert(self.keys, id) 91 | table.sort(self.keys) 92 | end 93 | 94 | local priority = self.priorities[id] 95 | if not priority then 96 | self.priorities[id] = {} 97 | priority = self.priorities[id] 98 | end 99 | 100 | local pos = #priority + 1 101 | 102 | table.insert(priority, setmetatable({ 103 | priority = id, 104 | destroy = function (hself) 105 | self.priorities[hself.priority][pos] = nil 106 | end, 107 | targets = {[name] = true} 108 | }, { 109 | __call = handler 110 | })) 111 | 112 | return self.priorities[id][pos] 113 | end, 114 | 115 | event = function (self, name) 116 | return setmetatable({}, { 117 | __call = function (cls, data, once) 118 | local inst = setmetatable({ 119 | get = function (self) 120 | return self.data 121 | end, 122 | cancel = function (self) 123 | self.canceled = true 124 | end 125 | }, { 126 | __index = function (self, k) 127 | local v = rawget(cls, k) 128 | if v then 129 | return v 130 | else 131 | local v = rawget(self, k) 132 | return v and v or rawget(self.data, k) 133 | end 134 | end 135 | }) 136 | 137 | inst.name = name 138 | inst.data = data 139 | inst.once = once or false 140 | inst.canceled = false 141 | 142 | return inst 143 | end 144 | }) 145 | end, 146 | 147 | stdEvent = function (self, name, evt) 148 | local event = require("event") 149 | self.stdEvents = self.stdEvents or {} 150 | if self.stdEvents[name] then 151 | for _, hdlr in pairs(self.stdEvents[name]) do 152 | if hdlr.event == evt then 153 | return 154 | end 155 | end 156 | end 157 | self.stdEvents[name] = self.stdEvents[name] or {} 158 | local handler = self.eventListener(evt) 159 | local pos = #self.stdEvents[name] + 1 160 | table.insert(self.stdEvents[name], { 161 | name = name, 162 | event = evt, 163 | handler = handler, 164 | destroy = function (hself) 165 | event.ignore(hself.name, hself.handler) 166 | self.stdEvents[hself.name][pos] = nil 167 | end 168 | }) 169 | event.listen(name, handler) 170 | return self.stdEvents[name][pos] 171 | end, 172 | 173 | timer = function (self, interval, e, times) 174 | local event = require("event") 175 | self.timers = self.timers or {} 176 | local timerFunction = function() 177 | self:push(e { 178 | time = os.time(), 179 | interval = interval 180 | }) 181 | end 182 | local pos = #self.timers + 1 183 | 184 | local id = event.timer(interval, timerFunction, times) 185 | 186 | table.insert(self.timers, { 187 | interval = interval, 188 | event = e, 189 | handler = timerFunction, 190 | id = id, 191 | 192 | destroy = function (hself) 193 | event.cancel(hself.id) 194 | self.timers[pos] = nil 195 | end 196 | }) 197 | 198 | return self.timers[pos] 199 | end 200 | }, meta) 201 | -------------------------------------------------------------------------------- /libbigint/README.md: -------------------------------------------------------------------------------- 1 | Originally written **not by me**. I've put it here and fixed things to make it work. 2 | 3 | # libbigint 4 | *May the big numbers be with you.* 5 | 6 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/libbigint). 7 | 8 | ## Description 9 | When required, the `bigint` function is returned for creation big integers. Those are the table values with a metatable assigned to them that provides some metamethods: 10 | 11 | * `__add` — the addition operation (`a + b`) 12 | * `__sub` — the subtraction operation (`a - b`) 13 | * `__mul` — the multiplication (`a * b`) 14 | * `__div` — the division (`a / b`) 15 | * `__mod` — the modulo (`a % b`) 16 | * `__unm` — the unary minus (inversion, `-a`) 17 | * `__eq` — test for equality (`a == b`) 18 | * `__lt` — the "less than" (or "greater than") comparsion (`a < b`, `b > a`) 19 | * `__le` — the "less/greater than or equal to" comparion (`a <= b`, `b >= a`) 20 | * `__tostring` returns the decimal number representation (as string): `tonumber(a)` 21 | 22 | As you can see, all Lua 5.2 operators are supported. 23 | -------------------------------------------------------------------------------- /libcsv/README.md: -------------------------------------------------------------------------------- 1 | # libcsv 2 | *A CSV parser* 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/libcsv). 5 | 6 | ## Description 7 | Returns a function that accepts the string to decode as the first argument and returns the decoded value. 8 | 9 | ```lua 10 | local parse = require("csv") 11 | print(require("serialization").serialize(parse("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh"))) 12 | ``` 13 | 14 | Fully implements the [RFC 4180](https://tools.ietf.org/html/rfc4180). 15 | -------------------------------------------------------------------------------- /libcsv/csv.lua: -------------------------------------------------------------------------------- 1 | local function parseCSV(s) 2 | result = {} 3 | row = {} 4 | cell = "" 5 | quoted = false 6 | prevQuote = false 7 | 8 | for i = 1, #s, 1 do 9 | c = s:sub(i, i) 10 | if quoted then 11 | if c == '"' then 12 | prevQuote = true 13 | quoted = false 14 | else 15 | cell = cell .. c 16 | end 17 | else 18 | if c == '"' then 19 | if #cell == 0 then 20 | quoted = true 21 | else 22 | if prevQuote then 23 | cell = cell .. '"' 24 | quoted = true 25 | prevQuote = false 26 | else 27 | return false 28 | end 29 | end 30 | elseif c == "," then 31 | table.insert(row, cell) 32 | cell = "" 33 | prevQuote = false 34 | elseif c == "\n" then 35 | table.insert(row, cell) 36 | cell = "" 37 | table.insert(result, row) 38 | row = {} 39 | prevQuote = false 40 | else 41 | if prevQuote then 42 | return false 43 | end 44 | cell = cell .. c 45 | end 46 | end 47 | end 48 | 49 | if #cell ~= 0 then 50 | if quoted then 51 | return false 52 | end 53 | table.insert(row, cell) 54 | table.insert(result, row) 55 | end 56 | 57 | return result 58 | end 59 | 60 | local function test() 61 | local p = function(s) 62 | print(require("serialization").serialize(s)) 63 | end 64 | 65 | p(parseCSV( 66 | [[ 67 | aaa,bbb,ccc,ddd 68 | eee,fff,ggg,hhh 69 | ]] 70 | )) 71 | p(parseCSV( 72 | [[ 73 | aaa,bbb,ccc,ddd 74 | eee,fff,ggg,hhh]] 75 | )) 76 | p(parseCSV( 77 | [[ 78 | aaa,bbb,ccc,"ddd 79 | eee",fff,ggg,hhh]] 80 | )) 81 | p(parseCSV( 82 | [[ 83 | aaa,bbb,c"cc,ddd 84 | eee,fff,ggg,hhh]] 85 | )) 86 | p(parseCSV( 87 | [[ 88 | aaa,bbb,"ccc,ddd 89 | eee,fff,ggg,hhh]] 90 | )) 91 | p(parseCSV( 92 | [[ 93 | aaa,bbb,"cc"c,ddd 94 | eee,fff,ggg,hhh]] 95 | )) 96 | p(parseCSV( 97 | [[ 98 | aaa,bbb,"cc""c,ddd 99 | eee,fff,ggg,hhh]] 100 | )) 101 | p(parseCSV( 102 | [[ 103 | aaa,bbb,"cc""c",ddd 104 | eee,fff,ggg,hhh]] 105 | )) 106 | end 107 | 108 | return parseCSV 109 | -------------------------------------------------------------------------------- /libder-decoder/README.md: -------------------------------------------------------------------------------- 1 | # der-decoder 2 | *Suprisingly, it does what the name says: decodes DER-encoded data*. 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/libder-decoder). 5 | 6 | ## Description 7 | The library returns a decoder function: `decode(data: string, kwargs: table)` 8 | 9 | The first argument is the data that should be decoded. The second argument specifies additional parameters for the decoder. 10 | 11 | Currently, only the `content` field is used: it's the table of tag codes that will be used in specified order. 12 | 13 | E.g., if you run this piece of code: 14 | 15 | ```lua 16 | decode(data, {context={ 17 | 0x02, 18 | 0x09 19 | }}) 20 | ``` 21 | 22 | the first tag with the "context-specific" class will be decoded as an integer, and the second one will be decoded as a real number. 23 | 24 | ### Usage 25 | I use this library to decode X.509 certificates (the standard of a very common certificate encoding, e.g., used in TLS). You, though, may have your own ideas on how to use this library. :) 26 | 27 | I'd not recommend to encode something with the DER, though, as it's not that space-efficient. 28 | 29 | ## License 30 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 31 | -------------------------------------------------------------------------------- /libhttp/README.md: -------------------------------------------------------------------------------- 1 | # libhttp 2 | *A HTTP/HTTPS 1.1 library.* 3 | 4 | This is a library (obviously) that returns a HTTP request function. It tries to mimic behaviour of `component.internet.request`, at the same time enabling to choose a request method to use (`GET`, `POST`, `PATCH`, etc.). 5 | 6 | It's much slower than the default-provided request function. Use **only** (!) if you need to use a request method different from `GET` and `POST`. 7 | 8 | ## Usage 9 | 10 | * `http(url: string[, body: string[, headers: table[, method: string]]])` — starts a new request. 11 | * `http(kwargs: table)` — does pretty much the same, but accepts a table of arguments. 12 | 13 | All options of kwargs, except `url`, are optional. Here's the example of a full kwargs table: 14 | 15 | ```lua 16 | { 17 | url = "https://example.com/", 18 | body = "Hi there\n", 19 | headers = { 20 | ["Content-Type"] = "application/json", 21 | }, 22 | method = "PATCH" 23 | } 24 | ``` 25 | 26 | The function returns a table of functions: 27 | 28 | * `response.close()` — closes the connection. 29 | * `response.finishConnect(): boolean[, string]` — returns whether the connection is established. 30 | * `response.read([n: number]): string or nil` — reads a certain amount of data from the buffer. 31 | * `response.response(): number, string, table` — returns a status code, status text, and headers. 32 | * `response.write(data: string)` — writes a data string to the socket stream. 33 | 34 | All headers returned by `response.response()` are Train-Cased, so that you don't need to worry about that. 35 | 36 | The response body is read all and stored in the buffer. If you don't specify any arguments for `response.read`, it will return *the whole buffer*. This is different from "vanilla" `request` method, where the data is returned as chunks of random length. 37 | 38 | ## License 39 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 40 | -------------------------------------------------------------------------------- /libsemver/README.md: -------------------------------------------------------------------------------- 1 | # libsemver 2 | *A parser of Semantic Version strings ported to MoonScript* 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/libsemver). 5 | 6 | Original version was written in Python and is available [here](https://git.io/vKTbW). 7 | 8 | Since it's a port, you can use the documentation [here](https://python-semanticversion.readthedocs.io/en/latest/). I didn't modify the way it functions. 9 | 10 | The `libsemver` package only contains transpiled Lua code. The sources are in the `libsemver-src` package. 11 | -------------------------------------------------------------------------------- /libtls13/.busted: -------------------------------------------------------------------------------- 1 | return { 2 | _all = { 3 | ROOT = {"test"}, 4 | pattern = ".%.lua$", 5 | verbose = true, 6 | }, 7 | 8 | default = { 9 | ["exclude-tags"] = {"long"}, 10 | }, 11 | 12 | all = { 13 | ["exclude-tags"] = {}, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libtls13/README.md: -------------------------------------------------------------------------------- 1 | # libtls13 2 | 3 | A TLS 1.3 client implementation for OpenComputers. 4 | 5 | Additionally: 6 | 7 | - DER decoder 8 | 9 | - X.509 certificate parser 10 | 11 | - Cryptographic library (pure Lua implementations): 12 | 13 | - RSA signature verification 14 | 15 | - AES ciphers (+ GCM) 16 | 17 | - ChaCha20 (stream cipher) and Poly1305 (MAC) 18 | 19 | - SHA2 family of hash functions 20 | 21 | - Curve25519: X25519 and Ed25519 (port of libsodium / ref10) 22 | 23 | - ECDSA (secp384r1) signature verification 24 | 25 | Requires a T2 data card for RNG. 26 | 27 | Needs Lua 5.3+. Lua 5.2 is unsupported and won't work. 28 | 29 | TLS support: 30 | 31 | - Can perform a certificate-only handshake (PSKs aren't supported). 32 | 33 | - Tolerates hello retry requests. 34 | 35 | - Supports client certificates (no idea why you'd need that, but it's there). 36 | 37 | - ALPN and server\_name extensions. 38 | 39 | - Only does TLS 1.3. If you need TLS 1.2, see `libtls` in this repository. 40 | And if you need TLS 1.1-, you don't. 41 | 42 | Crypto: 43 | 44 | - Cipher suites: 45 | 46 | - `TLS_AES_128_GCM_SHA256` 47 | - `TLS_AES_256_GCM_SHA384` 48 | - `TLS_CHACHA20_POLY1305_SHA256` 49 | 50 | - Signature algorithms: 51 | 52 | - `rsa_pss_rsae_sha256`, `rsa_pss_rsae_sha384`, `rsa_pss_rsae_sha512` 53 | - `rsa_pss_pss_sha256`, `rsa_pss_pss_sha384`, `rsa_pss_pss_sha512` 54 | - `ecdsa_secp256r1_sha256` (needs a T3 data card), `ecdsa_secp384r1_sha384` 55 | - `ed25519` 56 | 57 | - Key exchange (groups): 58 | 59 | - `secp256r1`, `secp384r1` (both need a T3 data card) 60 | - `x25519` 61 | 62 | Assumes blocking I/O for simplicity. 63 | Since it's Lua, use coroutines if you don't like that. 64 | (Wrap a dummy "socket" that yields when reading/writing data.) 65 | 66 | ## Examples 67 | See `example/client.lua`. 68 | 69 | ## Documentation 70 | Everything that matters has a doc comment in the code. 71 | The main module (`src/init.lua`) in particular. 72 | 73 | The example program is also heavily documented. 74 | 75 | ## Tests 76 | There are a few tests for the crypto stuff. 77 | You'll need `busted` to run them. 78 | Don't forget to fetch the submodules: I'm using test vectors from Google's 79 | Project Wycheproof and a JSON library to parse test data. 80 | 81 | ## Security 82 | It does consistency checks mandated by the RFC. 83 | But then it also blindly believes whatever's written in certificates. 84 | MITM is thus trivial. 85 | 86 | (I did think about implementing cert validation this time, hence the gigantic 87 | X.509 parser, but when I read about DN matching, I decided it wasn't worth my 88 | time. 89 | You may want to try your hand at it if you ever wished to make your own icu in 90 | Lua.) 91 | 92 | RSA is notorious for being hard to get right. 93 | I didn't even try. 94 | 95 | ## Performance 96 | Fast enough. 97 | With RSA certificates handshakes are about 0.25 s longer. 98 | If you're in control of your server, try using certs with EC public keys. 99 | 100 | ## Future work 101 | Not that I'm planning to do it. 102 | But if you have a free summer or two, here's what would be nice: 103 | 104 | - Certification path validation. Pretty tough. 105 | - PSK support and 0-RTT data. A bit tricky. 106 | - Integrate TLS 1.2. Roughly two weeks of work, immense benefits. 107 | 108 | ## Why 109 | You could use this to connect to newer servers that want ephemeral DH. 110 | 111 | I just wanted to check out what's new in 1.3, implement finite field / modular 112 | arithmetic in Lua, and write a bunch of parsers. 113 | Also finally learned the order of precedence of bitwise operators. 114 | -------------------------------------------------------------------------------- /libtls13/example/client.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBgDCCASegAwIBAgIUTkLX3FiZk/G9aCcoqxEnKXIWRpEwCgYIKoZIzj0EAwIw 3 | FjEUMBIGA1UEAwwLSGVsbG8gd29ybGQwHhcNMjMwNzA2MTcxNTE3WhcNMjMxMDE0 4 | MTcxNTE3WjAWMRQwEgYDVQQDDAtIZWxsbyB3b3JsZDBZMBMGByqGSM49AgEGCCqG 5 | SM49AwEHA0IABHH0aXZKT0W0+YqSTBFzyMLOmHiT+NLHZFc8s8ALB019SSHKzTK2 6 | CQ0tqQPLTblmm4rl7ptB75z3nCltX40nuPijUzBRMB0GA1UdDgQWBBQOlPK0eL6d 7 | RNKRuNYyoAH56AKIYjAfBgNVHSMEGDAWgBQOlPK0eL6dRNKRuNYyoAH56AKIYjAP 8 | BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCIAxsfiq6rLrLuOfUNAEa 9 | gfVk4brjPmRp3UcdYPYOPfMsAiApCnNyLYYuctuuvw9+tWXcIcw3+vHDSqgzd1kk 10 | 5J7IsQ== 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /libtls13/example/priv.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenPrograms/Fingercomp-Programs/a04d5c2ecd0ae11e3199cbfe49ce07b748719213/libtls13/example/priv.der -------------------------------------------------------------------------------- /libtls13/example/pub.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenPrograms/Fingercomp-Programs/a04d5c2ecd0ae11e3199cbfe49ce07b748719213/libtls13/example/pub.der -------------------------------------------------------------------------------- /libtls13/script/check-programs-cfg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | diff -u0 \ 4 | <(find src example README.md -type f | sort) \ 5 | <((echo -n 'for k in pairs(('; cat ../programs.cfg; echo ').libtls13.files) do print(k:match("[^/]+/[^/]+/(.+)")) end') | lua - | sort) 6 | -------------------------------------------------------------------------------- /libtls13/script/generate-div64by32-test-data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import random 4 | 5 | N = 1024 6 | 7 | for _ in range(N): 8 | dividend = random.randint(0, (1 << 64) - 1) | 1 << 63 9 | divisor = random.randint(0, (1 << 32) - 1) | 1 << 31 10 | quotient = dividend // divisor 11 | 12 | print(f"0x{dividend:x} 0x{divisor:x} 0x{quotient:x}") 13 | -------------------------------------------------------------------------------- /libtls13/script/generate-mod-vec-vec-test-data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import random 4 | 5 | N = 1024 6 | MAX_WORDS = 1024 7 | 8 | for _ in range(N): 9 | x_words = random.randint(1, MAX_WORDS) 10 | y_words = random.randint(1, MAX_WORDS) 11 | x = 0 12 | y = 0 13 | 14 | for _ in range(x_words): 15 | x <<= 32 16 | x |= random.getrandbits(32) 17 | 18 | for _ in range(y_words): 19 | y <<= 32 20 | y |= random.getrandbits(32) 21 | 22 | if y == 0: 23 | y = 1 24 | 25 | remainder = x % y 26 | 27 | print(f"{x:x} {y:x} {remainder:x}") 28 | -------------------------------------------------------------------------------- /libtls13/script/generate-modpow-test-data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import random 4 | 5 | N = 64 6 | MAX_WORDS = 256 7 | MAX_EXPONENT_WORDS = 8 8 | 9 | for _ in range(N): 10 | x_words = random.randint(1, MAX_WORDS) 11 | y_words = random.randint(1, MAX_EXPONENT_WORDS) 12 | m_words = random.randint(1, MAX_WORDS) 13 | x = 0 14 | y = 0 15 | m = 0 16 | 17 | for _ in range(x_words): 18 | x <<= 32 19 | x |= random.getrandbits(32) 20 | 21 | for _ in range(y_words): 22 | y <<= 32 23 | y |= random.getrandbits(32) 24 | 25 | for _ in range(m_words): 26 | m <<= 32 27 | m |= random.getrandbits(32) 28 | 29 | m |= 1 30 | 31 | result = pow(x, y, m) 32 | print(f"{x:x} {y:x} {m:x} {result:x}") 33 | -------------------------------------------------------------------------------- /libtls13/script/generate-sha-hashes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import hashlib 4 | import random 5 | import sys 6 | 7 | match sys.argv[1] if len(sys.argv) >= 2 else None: 8 | case "sha256": 9 | algo = hashlib.sha256 10 | 11 | case "sha384": 12 | algo = hashlib.sha384 13 | 14 | case "sha512": 15 | algo = hashlib.sha512 16 | 17 | case _: 18 | print("Usage: ./script/generate-sha-hashes.py {sha256|sha384|sha512}") 19 | sys.exit(1) 20 | 21 | sizes = list(range(1, 256)) + [256, 512, 1024, 2048, 4096, 8192] 22 | 23 | for size in sizes: 24 | data = random.randbytes(size) 25 | print(data.hex(), algo(data).hexdigest()) 26 | -------------------------------------------------------------------------------- /libtls13/src/asn/encode.lua: -------------------------------------------------------------------------------- 1 | -- Utilities for encoding ASN.1 values. 2 | 3 | local asn = require("tls13.asn") 4 | local util = require("tls13.util") 5 | 6 | local lib = {} 7 | 8 | function lib.encodeAsnValue(tag, encoding, contents) 9 | -- it's not even hard to support them, but I don't need that, so I don't. 10 | assert(tag.num < 0x1f, "long tags are not supported") 11 | 12 | local length = #contents 13 | local initialOctet = tag.num 14 | 15 | if encoding == "primitive" then 16 | -- do nothing, pretty much. 17 | elseif encoding == "constructed" then 18 | initialOctet = initialOctet | 0x20 19 | else 20 | error("unknown encoding: " .. encoding) 21 | end 22 | 23 | if tag.class == "universal" then 24 | -- do nothing 25 | elseif tag.class == "application" then 26 | initialOctet = initialOctet | 0x40 27 | elseif tag.class == "contextSpecific" then 28 | initialOctet = initialOctet | 0x80 29 | elseif tag.class == "private" then 30 | initialOctet = initialOctet | 0xc0 31 | else 32 | error("unknown class: " .. tag.class) 33 | end 34 | 35 | if length < 0x80 then 36 | return (">B s1"):pack(initialOctet, contents) 37 | end 38 | 39 | local lengthSize = util.idivCeil(util.lastLeadingZero(length), 8) 40 | assert(lengthSize < 0x7f, "size is too large") 41 | 42 | return (">B B I" .. lengthSize):pack( 43 | initialOctet, 44 | 0x80 | lengthSize, 45 | length 46 | ) .. contents 47 | end 48 | 49 | function lib.encodeVarint(n) 50 | local bytes = {} 51 | 52 | repeat 53 | local byte = n & 0x7f 54 | 55 | if #bytes > 0 then 56 | byte = byte | 0x80 57 | end 58 | 59 | table.insert(bytes, ("B"):pack(byte)) 60 | n = n >> 7 61 | until n == 0 62 | 63 | -- swap bytes (varints are big-endian) 64 | util.reverse(bytes) 65 | 66 | return table.concat(bytes) 67 | end 68 | 69 | function lib.encodeObjectIdentifier(oid) 70 | oid = util.copy(oid) 71 | local firstComponent = table.remove(oid, 1) 72 | oid[1] = oid[1] + firstComponent * 40 73 | 74 | for i = 1, #oid, 1 do 75 | oid[i] = lib.encodeVarint(oid[i]) 76 | end 77 | 78 | return lib.encodeAsnValue( 79 | asn.asnTags.universal.objectIdentifier, 80 | "primitive", 81 | table.concat(oid) 82 | ) 83 | end 84 | 85 | function lib.encodeSequence(values) 86 | return lib.encodeAsnValue( 87 | asn.asnTags.universal.sequence, 88 | "constructed", 89 | table.concat(values) 90 | ) 91 | end 92 | 93 | function lib.encodeOctetString(s) 94 | return lib.encodeAsnValue( 95 | asn.asnTags.universal.octetString, 96 | "primitive", 97 | s 98 | ) 99 | end 100 | 101 | function lib.encodeNull() 102 | return lib.encodeAsnValue( 103 | asn.asnTags.universal.null, 104 | "primitive", 105 | "" 106 | ) 107 | end 108 | 109 | function lib.encodeBitString(bits) 110 | return lib.encodeAsnValue( 111 | asn.asnTags.universal.bitString, 112 | "primitive", 113 | string.char(bits:padding() & 0x7) .. bits:toBytes() 114 | ) 115 | end 116 | 117 | return lib 118 | -------------------------------------------------------------------------------- /libtls13/src/asn/oid.lua: -------------------------------------------------------------------------------- 1 | -- Recognized ASN.1 OIDs. 2 | 3 | local asn = require("tls13.asn") 4 | 5 | local lib = {} 6 | 7 | lib.root = asn.makeOid() 8 | 9 | lib.ccitt = lib.root.ccitt(0) 10 | -- this one has a fun bit of history 11 | lib.domainComponent = lib 12 | .ccitt 13 | .data(9) 14 | .pss(2342) 15 | .ucl(19200300) 16 | .pilot(100) 17 | .pilotAttributeType(1) 18 | .domainComponent(25) 19 | 20 | lib.iso = lib.root.iso(1) 21 | 22 | lib.internet = lib 23 | .iso 24 | .identifiedOrganization(3) 25 | .dod(6) 26 | .internet(1) 27 | 28 | lib.pkix = lib 29 | .internet 30 | .security(5) 31 | .mechanisms(5) 32 | .pkix(7) 33 | 34 | lib.pkix.pe(1) 35 | lib.pkix.pe.authorityInfoAccess(1) 36 | lib.pkix.pe.subjectInfoAccess(11) 37 | 38 | lib.pkix.qt(2) 39 | lib.pkix.qt.cps(1) 40 | lib.pkix.qt.unotice(2) 41 | 42 | lib.pkix.kp(3) 43 | lib.pkix.kp.serverAuth(1) 44 | lib.pkix.kp.clientAuth(2) 45 | lib.pkix.kp.codeSigning(3) 46 | lib.pkix.kp.emailProtection(4) 47 | lib.pkix.kp.timeStamping(5) 48 | lib.pkix.kp.OCSPSigning(9) 49 | 50 | lib.pkix.ad(48) 51 | lib.pkix.ad.ocsp(1) 52 | lib.pkix.ad.caIssuers(2) 53 | lib.pkix.ad.timeStamping(3) 54 | lib.pkix.ad.caRepository(5) 55 | 56 | lib.x25519 = lib.iso.identifiedOrganization.thawte(101).x25519(110) 57 | lib.edDSA25519 = lib.iso.identifiedOrganization.thawte(101).edDSA25519(112) 58 | 59 | lib.pkcs1 = lib 60 | .iso 61 | .memberBody(2) 62 | .us(840) 63 | .rsadsi(113549) 64 | .pkcs(1) 65 | .pkcs1(1) 66 | 67 | lib.pkcs1.rsaEncryption(1) 68 | lib.pkcs1.sha1WithRSAEncryption(5) 69 | lib.pkcs1.mgf1(8) 70 | lib.pkcs1.rsassaPss(10) 71 | lib.pkcs1.sha256WithRSAEncryption(11) 72 | lib.pkcs1.sha384WithRSAEncryption(12) 73 | lib.pkcs1.sha512WithRSAEncryption(13) 74 | 75 | lib.ansiX962 = lib.iso.memberBody.us(840).ansiX962(10045) 76 | 77 | lib.ansiX962.keyType(2).ecPublicKey(1) 78 | 79 | lib.ansiX962.curves(3).prime(1).prime256r1(7) 80 | 81 | lib.ansiX962.signatures(4).ecdsaWithSHA2(3).ecdsaWithSHA256(2) 82 | lib.ansiX962.signatures(4).ecdsaWithSHA2(3).ecdsaWithSHA384(3) 83 | lib.ansiX962.signatures(4).ecdsaWithSHA2(3).ecdsaWithSHA512(4) 84 | 85 | lib.sha1 = lib 86 | .iso 87 | .identifiedOrganization(3) 88 | .oiw(14) 89 | .secsig(3) 90 | .algorithms(3) 91 | .sha1(26) 92 | 93 | lib.iso.identifiedOrganization.certicom(132).curve(0).ansip384r1(34) 94 | 95 | lib.jointIsoCcitt = lib.root.jointIsoCcitt(2) 96 | 97 | lib.ce = lib.jointIsoCcitt.ds(5).certificateExtension(29) 98 | lib.ce.subjectDirectoryAttributes(9) 99 | lib.ce.subjectKeyIdentifier(14) 100 | lib.ce.keyUsage(15) 101 | lib.ce.subjectAltName(17) 102 | lib.ce.issuerAltName(18) 103 | lib.ce.basicConstraints(19) 104 | lib.ce.nameConstraints(30) 105 | lib.ce.cRLDistributionPoints(31) 106 | lib.ce.certificatePolicies(32) 107 | lib.ce.certificatePolicies.anyPolicy(0) 108 | lib.ce.policyMappings(33) 109 | lib.ce.authorityKeyIdentifier(35) 110 | lib.ce.policyConstraints(36) 111 | lib.ce.extKeyUsage(37) 112 | lib.ce.extKeyUsage.anyExtendedKeyUsage(0) 113 | lib.ce.inhibitAnyPolicy(54) 114 | 115 | lib.at = lib.jointIsoCcitt.ds.at(4) 116 | lib.at.commonName(3) 117 | lib.at.surname(4) 118 | lib.at.serialNumber(5) 119 | lib.at.countryName(6) 120 | lib.at.localityName(7) 121 | lib.at.stateOrProvinceName(8) 122 | lib.at.organizationName(10) 123 | lib.at.organizationalUnitName(11) 124 | lib.at.title(12) 125 | lib.at.name(41) 126 | lib.at.givenName(42) 127 | lib.at.initials(43) 128 | lib.at.generationQualifier(44) 129 | lib.at.dnQualifier(46) 130 | lib.at.pseudonym(65) 131 | 132 | lib.hashalgs = lib 133 | .jointIsoCcitt 134 | .country(16) 135 | .us(840) 136 | .organization(1) 137 | .gov(101) 138 | .csor(3) 139 | .nistalgorithm(4) 140 | .hashalgs(2) 141 | 142 | lib.hashalgs.sha256(1) 143 | lib.hashalgs.sha384(2) 144 | lib.hashalgs.sha512(3) 145 | 146 | return lib 147 | -------------------------------------------------------------------------------- /libtls13/src/base64.lua: -------------------------------------------------------------------------------- 1 | -- Base64 encoding facilities. 2 | 3 | local util = require("tls13.util") 4 | 5 | local lib = {} 6 | 7 | lib.defaultAlphabet = {} 8 | 9 | for i = 1, 26, 1 do 10 | table.insert(lib.defaultAlphabet, string.char(("A"):byte() + i - 1)) 11 | end 12 | 13 | for i = 1, 26, 1 do 14 | table.insert(lib.defaultAlphabet, string.char(("a"):byte() + i - 1)) 15 | end 16 | 17 | for i = 0, 9, 1 do 18 | table.insert(lib.defaultAlphabet, string.char(("0"):byte() + i)) 19 | end 20 | 21 | table.insert(lib.defaultAlphabet, "+") 22 | table.insert(lib.defaultAlphabet, "/") 23 | 24 | lib.defaultInverseAlphabet = util.swapPairs(lib.defaultAlphabet) 25 | 26 | local function calculatePadding(encoded) 27 | local count = 0 28 | 29 | for i = #encoded, 1, -1 do 30 | if encoded:sub(i, i) == "=" then 31 | count = count + 1 32 | else 33 | break 34 | end 35 | end 36 | 37 | if count > 2 then 38 | return nil, "invalid padding" 39 | end 40 | 41 | return count 42 | end 43 | 44 | function lib.decode(encoded, inverseAlphabet) 45 | inverseAlphabet = inverseAlphabet or lib.defaultInverseAlphabet 46 | 47 | if #encoded % 4 ~= 0 then 48 | return nil, "base64-encoded data length must be divisible by 4" 49 | end 50 | 51 | local padding, err = calculatePadding(encoded) 52 | 53 | if not padding then 54 | return nil, err 55 | end 56 | 57 | local result = {} 58 | 59 | for i = 1, #encoded, 4 do 60 | local bits = 0 61 | 62 | for j = 1, 4, 1 do 63 | local pos = i + j - 1 64 | local byte = encoded:sub(pos, pos) 65 | 66 | if byte == "=" and pos > #encoded - padding then 67 | byte = 0 68 | elseif byte == "=" then 69 | return nil, "found equals sign not part of padding at pos " .. pos 70 | else 71 | byte = inverseAlphabet[byte] - 1 72 | end 73 | 74 | bits = bits << 6 | byte 75 | end 76 | 77 | local bytes = 3 78 | 79 | if i + 4 > #encoded then 80 | bytes = 3 - padding 81 | end 82 | 83 | table.insert(result, (">BBB"):sub(1, bytes + 1):pack( 84 | bits >> 16, 85 | bits >> 8 & 0xff, 86 | bits & 0xff 87 | )) 88 | end 89 | 90 | return table.concat(result) 91 | end 92 | 93 | function lib.encode(data, alphabet) 94 | alphabet = alphabet or lib.defaultAlphabet 95 | 96 | local result = {} 97 | 98 | for i = 1, #data, 3 do 99 | if i + 2 <= #data then 100 | local bits = (">I3"):unpack(data, i) 101 | result[i + 0] = alphabet[bits >> 18] 102 | result[i + 1] = alphabet[bits >> 12 & 0x3f] 103 | result[i + 2] = alphabet[bits >> 6 & 0x3f] 104 | result[i + 3] = alphabet[bits & 0x3f] 105 | elseif i + 1 <= #data then 106 | bits = (">I2"):unpack(data, i) 107 | result[i + 0] = alphabet[bits >> 10] 108 | result[i + 1] = alphabet[bits >> 4 & 0x3f] 109 | result[i + 2] = alphabet[bits << 2 & 0x3f] 110 | result[i + 3] = "=" 111 | else 112 | bits = data:byte(i) 113 | result[i + 0] = alphabet[bits >> 2] 114 | result[i + 1] = alphabet[bits << 4 & 0x3f] 115 | result[i + 2] = "=" 116 | result[i + 3] = "=" 117 | end 118 | end 119 | 120 | return table.concat(result) 121 | end 122 | 123 | return lib 124 | -------------------------------------------------------------------------------- /libtls13/src/crypto/hkdf.lua: -------------------------------------------------------------------------------- 1 | -- The HMAC-based key derivation function. 2 | -- 3 | -- Ref: 4 | -- - RFC 5869. https://rfc-editor.org/rfc/rfc5869.html 5 | 6 | local lib = {} 7 | 8 | local meta = { 9 | __index = { 10 | extract = function(self, ikm, salt) 11 | if not salt then 12 | salt = ("\0"):rep(self.__hmac.HASH_SIZE) 13 | end 14 | 15 | return self.__hmac(ikm, salt) 16 | end, 17 | 18 | expand = function(self, context, len, key) 19 | local hashLen = self.__hmac.HASH_SIZE 20 | assert(#key >= hashLen) 21 | assert(len <= hashLen * 255) 22 | 23 | local chunks = {} 24 | 25 | for i = 1, len, hashLen do 26 | table.insert(chunks, self.__hmac( 27 | ("%s%s%s"):format( 28 | chunks[#chunks] or "", 29 | context, 30 | string.char(#chunks + 1) 31 | ), 32 | key 33 | )) 34 | end 35 | 36 | local resultLen = #chunks * hashLen 37 | 38 | if len < resultLen then 39 | chunks[#chunks] = chunks[#chunks]:sub(1, -(resultLen - len) - 1) 40 | end 41 | 42 | local result = table.concat(chunks) 43 | 44 | return result 45 | end, 46 | }, 47 | } 48 | 49 | function lib.hkdf(hmac) 50 | return setmetatable({ 51 | __hmac = hmac, 52 | }, meta) 53 | end 54 | 55 | return lib 56 | -------------------------------------------------------------------------------- /libtls13/src/crypto/hmac.lua: -------------------------------------------------------------------------------- 1 | -- The HMAC function. 2 | -- 3 | -- Ref: 4 | -- - RFC 2104. https://www.rfc-editor.org/rfc/rfc2104.html 5 | 6 | local lib = {} 7 | 8 | local function xorByteString(s, byte) 9 | return ( 10 | s:gsub("()(.)", function(i, c) 11 | return string.char(c:byte() ~ byte) 12 | end) 13 | ) 14 | end 15 | 16 | -- Creates a HMAC function with the given `hash` function. 17 | -- 18 | -- The returned function is of signature `function(data, key)`. 19 | function lib.hmac(hash) 20 | local blockSize = hash.BLOCK_SIZE 21 | 22 | local function hmac(self, data, key) 23 | if #key > blockSize then 24 | key = hash():update(key):finish() 25 | end 26 | 27 | local digest = hash() 28 | :update(xorByteString(key, 0x36) .. ("\x36"):rep(blockSize - #key)) 29 | :update(data) 30 | :finish() 31 | 32 | return hash() 33 | :update(xorByteString(key, 0x5c) .. ("\x5c"):rep(blockSize - #key)) 34 | :update(digest) 35 | :finish() 36 | end 37 | 38 | return setmetatable({ 39 | BLOCK_SIZE = blockSize, 40 | HASH_SIZE = hash.HASH_SIZE, 41 | }, {__call = hmac}) 42 | end 43 | 44 | return lib 45 | -------------------------------------------------------------------------------- /libtls13/src/group.lua: -------------------------------------------------------------------------------- 1 | -- Named groups for key exchange. 2 | 3 | local curve25519 = require("tls13.crypto.curve25519") 4 | 5 | local lib = {} 6 | 7 | function lib.makeX25519(rng) 8 | local keyGen = curve25519.makeX25519KeyGen(rng) 9 | 10 | return { 11 | decodePublicKey = function(self, keyExchange) 12 | if #keyExchange ~= 32 then 13 | return nil, "invalid share" 14 | end 15 | 16 | return {public = keyExchange} 17 | end, 18 | 19 | encodePublicKey = function(self, keys) 20 | return keys.public 21 | end, 22 | 23 | generateKeyPair = function(self) 24 | return keyGen() 25 | end, 26 | 27 | deriveSharedSecret = function(self, clientShare, serverShare) 28 | return curve25519.deriveSharedSecret(clientShare, serverShare) 29 | end, 30 | } 31 | end 32 | 33 | return lib 34 | -------------------------------------------------------------------------------- /libtls13/src/io.lua: -------------------------------------------------------------------------------- 1 | -- A buffered I/O wrapper. 2 | 3 | local buffer = require("tls13.util.buffer") 4 | 5 | local lib = {} 6 | 7 | local meta = { 8 | __index = { 9 | read = function(self, n) 10 | if not n and self.__buf:remaining() > 0 then 11 | return self:read(self.__buf:remaining()) 12 | end 13 | 14 | while not n or n > self.__buf:remaining() do 15 | local chunk, err = self.__f:read(n and (n - self.__buf:remaining())) 16 | 17 | if not chunk then 18 | return nil, err 19 | elseif #chunk > 0 then 20 | n = n or #chunk 21 | self.__buf:append(chunk) 22 | end 23 | end 24 | 25 | return self.__buf:consume(n) 26 | end, 27 | 28 | write = function(self, data) 29 | local remaining = data 30 | 31 | while #remaining > 0 do 32 | local written, err = self.__f:write(remaining) 33 | 34 | if not written then 35 | return nil, err 36 | end 37 | 38 | remaining = remaining:sub(written + 1) 39 | end 40 | 41 | return #data 42 | end, 43 | 44 | close = function(self) 45 | self.__f:close() 46 | end, 47 | 48 | readUnpack = function(self, fmt) 49 | local size = string.packsize(fmt) 50 | local data, err = self:read(size) 51 | 52 | if not data then 53 | return nil, err 54 | end 55 | 56 | local result = table.pack(string.unpack(fmt, data)) 57 | 58 | return table.unpack(result, 1, result.n - 1) 59 | end, 60 | 61 | inner = function(self) 62 | return self.__f 63 | end, 64 | }, 65 | } 66 | 67 | function lib.wrap(f) 68 | return setmetatable({ 69 | __f = f, 70 | __buf = buffer.makeBuffer(), 71 | }, meta) 72 | end 73 | 74 | return lib 75 | -------------------------------------------------------------------------------- /libtls13/src/oc.lua: -------------------------------------------------------------------------------- 1 | -- OpenComputers-specific code. 2 | 3 | local com = require("component") 4 | 5 | local lib = {} 6 | 7 | local function getDataCardTier(addr) 8 | local methods = com.methods(addr) 9 | 10 | if methods.ecdh ~= nil then 11 | return 3 12 | elseif methods.random ~= nil then 13 | return 2 14 | elseif methods.encode64 ~= nil then 15 | return 1 16 | else 17 | return 0 18 | end 19 | end 20 | 21 | function lib.getDataCard(tier) 22 | tier = tier or 3 23 | 24 | return assert( 25 | lib.getDataCardOrNil(tier), 26 | ("T%d data card required"):format(tier) 27 | ) 28 | end 29 | 30 | function lib.getDataCardOrNil(tier) 31 | tier = tier or 3 32 | 33 | for addr in com.list("data", true) do 34 | if getDataCardTier(addr) >= tier then 35 | return com.proxy(addr) 36 | end 37 | end 38 | 39 | return nil 40 | end 41 | 42 | return lib 43 | -------------------------------------------------------------------------------- /libtls13/src/oc/group.lua: -------------------------------------------------------------------------------- 1 | -- OpenComputers named groups for key exchange. 2 | 3 | local asn = require("tls13.asn") 4 | local asnEncode = require("tls13.asn.encode") 5 | local bitstring = require("tls13.asn.bitstring") 6 | local oc = require("tls13.oc") 7 | local oid = require("tls13.asn.oid") 8 | 9 | local lib = {} 10 | 11 | lib.curveOids = { 12 | [256] = oid.ansiX962.curves.prime.prime256r1, 13 | [384] = oid.iso.identifiedOrganization.certicom.curve.ansip384r1, 14 | } 15 | 16 | function lib.makeEcdhe(bitCount) 17 | local data = oc.getDataCard() 18 | local curveOid = assert(lib.curveOids[bitCount]) 19 | 20 | return { 21 | decodePublicKey = function(self, keyExchange) 22 | -- we have to encode as SubjectPublicKeyInfo so that OC accepts 23 | -- the point... 24 | -- rather wasteful. 25 | 26 | -- keyExchange is an EC point in the uncompressed format 27 | -- (RFC 8446, §4.2.8.2) 28 | -- X.509 subjectPublicKey stores this immediately as an OCTET STRING 29 | -- (RFC 5480, §2.2) 30 | local encoded = asnEncode.encodeSequence({ 31 | -- algorithm 32 | asnEncode.encodeSequence({ 33 | -- algorithm 34 | asnEncode.encodeObjectIdentifier(oid.ansiX962.keyType.ecPublicKey), 35 | -- parameters 36 | asnEncode.encodeObjectIdentifier(curveOid), 37 | }), 38 | -- subjectPublicKey 39 | asnEncode.encodeBitString(bitstring.fromBytes(keyExchange)), 40 | }) 41 | 42 | local key, err = data.deserializeKey(encoded, "ec-public") 43 | 44 | if not key then 45 | return nil, err 46 | end 47 | 48 | return {public = key} 49 | end, 50 | 51 | encodePublicKey = function(self, keys) 52 | -- note that Java 8 does not support the compressed form at all, 53 | -- so the serialized data is exactly what we need for TLS 54 | -- (after unwrapping the outer layers, of course). 55 | 56 | -- but if the produced value is something we don't expect, 57 | -- we'd like to abort the whole thing because it's not a user error 58 | local decoded = assert(asn.decode(keys.public.serialize())) 59 | assert(decoded.TAG == asn.asnTags.universal.sequence, "expected SEQUENCE") 60 | assert(#decoded == 2, "expected 2 top-level fields") 61 | 62 | local algorithm = decoded[1] 63 | local subjectPublicKey = decoded[2] 64 | 65 | assert( 66 | algorithm.TAG == asn.asnTags.universal.sequence, 67 | "algorithm must be SEQUENCE" 68 | ) 69 | assert( 70 | subjectPublicKey.TAG == asn.asnTags.universal.bitString, 71 | "subjectPublicKey must be BIT STRING" 72 | ) 73 | 74 | -- just make sure it's the curve we need... 75 | assert(#algorithm == 2, "algorithm must have 2 fields") 76 | local algorithmOid = algorithm[1] 77 | local parameters = algorithm[2] 78 | 79 | assert( 80 | algorithmOid.TAG == asn.asnTags.universal.objectIdentifier, 81 | "algorithm.algorithm must be OBJECT IDENTIFIER" 82 | ) 83 | assert( 84 | algorithmOid[1] == oid.ansiX962.keyType.ecPublicKey, 85 | "wrong algorithm.algorithm value (must be ecPublicKey)" 86 | ) 87 | assert( 88 | parameters.TAG == asn.asnTags.universal.objectIdentifier, 89 | "algorithm.parameters must be OBJECT IDENTIFIER" 90 | ) 91 | assert(parameters[1] == curveOid, "wrong curve OID") 92 | 93 | -- technically it could also be 0x00 (the point at infinity), 94 | -- but it's rather useless as a key 95 | assert( 96 | subjectPublicKey[1]:byte(1) == 0x04, 97 | "point format must be uncompressed" 98 | ) 99 | assert( 100 | subjectPublicKey[1]:isByteAligned(), 101 | "public key must be byte-aligned" 102 | ) 103 | 104 | -- the first byte describes the format, which we've already checked 105 | -- the rest are the X and Y coordinates, each `bitCount / 8` bytes long 106 | local encoded = subjectPublicKey[1]:toBytes() 107 | assert(#encoded - 1 == bitCount >> 3 << 1, "invalid length") 108 | 109 | return encoded 110 | end, 111 | 112 | generateKeyPair = function(self) 113 | local pubKey, privKey = data.generateKeyPair(bitCount) 114 | 115 | return { 116 | public = pubKey, 117 | private = privKey, 118 | } 119 | end, 120 | 121 | deriveSharedSecret = function(self, clientShare, serverShare) 122 | return data.ecdh(clientShare.private, serverShare.public) 123 | end, 124 | } 125 | end 126 | 127 | return lib 128 | -------------------------------------------------------------------------------- /libtls13/src/oc/io.lua: -------------------------------------------------------------------------------- 1 | local event = require("event") 2 | 3 | local lib = {} 4 | 5 | local POLL_TIMEOUT_SEC = 1 6 | 7 | local meta = { 8 | __index = { 9 | read = function(self, count) 10 | return self:_read(count, true) 11 | end, 12 | 13 | write = function(self, data) 14 | return self.__sock.write(data) 15 | end, 16 | 17 | close = function(self) 18 | return self.__sock.close() 19 | end, 20 | 21 | _read = function(self, count, poll) 22 | local chunk, err = self.__sock.read(count) 23 | 24 | if not chunk then 25 | return nil, err 26 | elseif chunk == "" and poll then 27 | -- internet_ready is good but unreliable 28 | -- and having timeouts is just good practice 29 | event.pull(POLL_TIMEOUT_SEC, "internet_ready", nil, self.__sock.id()) 30 | 31 | -- try fetching the data once more, 32 | -- but don't just get stuck here if we get nothing 33 | return self:_read(count, false) 34 | else 35 | return chunk 36 | end 37 | end, 38 | }, 39 | } 40 | 41 | function lib.wrap(sock) 42 | return setmetatable({ 43 | __sock = sock, 44 | }, meta) 45 | end 46 | 47 | return lib 48 | -------------------------------------------------------------------------------- /libtls13/src/oc/rng.lua: -------------------------------------------------------------------------------- 1 | -- OpenComputers random number generator. 2 | 3 | local oc = require("tls13.oc") 4 | 5 | local lib = {} 6 | 7 | function lib.rng(count) 8 | return oc.getDataCard(2).random(count) 9 | end 10 | 11 | return lib 12 | -------------------------------------------------------------------------------- /libtls13/src/oc/sigalg.lua: -------------------------------------------------------------------------------- 1 | -- OpenComputers signature algorithms. 2 | 3 | local asnEncode = require("tls13.asn.encode") 4 | local errors = require("tls13.error") 5 | local oc = require("tls13.oc") 6 | local oid = require("tls13.asn.oid") 7 | 8 | local lib = {} 9 | 10 | -- OC /does/ have secp384r1, but for unknowable reasons not ECDSA with sha384 11 | -- (not that it has sha384 at all anyway...) 12 | 13 | function lib.makeEcdsaSecp256r1SigAlg() 14 | local data = oc.getDataCard() 15 | 16 | return { 17 | decodePublicKey = function(self, pkInfo) 18 | if pkInfo.algorithm.algorithm ~= oid.ansiX962.keyType.ecPublicKey then 19 | return nil, errors.x509.publicKeyInvalid.subject( 20 | "algorithm OID is invalid" 21 | ) 22 | end 23 | 24 | if pkInfo.algorithm.parameters.namedCurve 25 | ~= oid.ansiX962.curves.prime.prime256r1 then 26 | return nil, errors.x509.publicKeyInvalid.subject("unsupported curve") 27 | end 28 | 29 | -- it's a bit wasteful to decode and then encode back... 30 | -- but whatever. 31 | local encoded = asnEncode.encodeSequence({ 32 | -- algorithm 33 | asnEncode.encodeSequence({ 34 | -- algorithm 35 | asnEncode.encodeObjectIdentifier(oid.ansiX962.keyType.ecPublicKey), 36 | -- parameters 37 | asnEncode.encodeObjectIdentifier( 38 | oid.ansiX962.curves.prime.prime256r1 39 | ), 40 | }), 41 | -- subjectPublicKey 42 | asnEncode.encodeBitString(pkInfo.subjectPublicKey), 43 | }) 44 | 45 | local key, err = data.deserializeKey(encoded, "ec-public") 46 | 47 | if not key then 48 | return nil, errors.x509.publicKeyInvalid.subject(err) 49 | end 50 | 51 | return key 52 | end, 53 | 54 | verify = function(self, publicKey, signedMessage, signature) 55 | return data.ecdsa(signedMessage, publicKey, signature) 56 | end, 57 | 58 | sign = function(self, privateKey, message) 59 | return data.ecdsa(message, privateKey) 60 | end, 61 | } 62 | end 63 | 64 | return lib 65 | -------------------------------------------------------------------------------- /libtls13/src/util/buffer.lua: -------------------------------------------------------------------------------- 1 | -- A byte buffer. 2 | -- 3 | -- Stores chunks in a table to optimize allocation. 4 | 5 | local util = require("tls13.util") 6 | 7 | local lib = {} 8 | 9 | local meta 10 | 11 | meta = { 12 | __index = { 13 | append = function(self, chunk) 14 | if #chunk > 0 then 15 | table.insert(self.__chunks, chunk) 16 | self.__remaining = self.__remaining + #chunk 17 | end 18 | end, 19 | 20 | peek = function(self, n) 21 | return self:_read(n, false) 22 | end, 23 | 24 | consume = function(self, n) 25 | return self:_read(n, true) 26 | end, 27 | 28 | copy = function(self) 29 | return setmetatable({ 30 | __chunks = util.copy(self.__chunks), 31 | __remaining = self.__remaining, 32 | __pos = self.__pos, 33 | }, meta) 34 | end, 35 | 36 | _read = function(self, n, consume) 37 | assert(n <= self.__remaining) 38 | 39 | local chunks = {} 40 | local consumed = 0 41 | local chunksConsumed = 0 42 | local lastChunkPos = 1 43 | 44 | for i, chunk in ipairs(self.__chunks) do 45 | local pos = 1 46 | 47 | if i == 1 and self.__pos > 1 then 48 | pos = self.__pos 49 | end 50 | 51 | if consumed + #chunk - pos + 1 > n then 52 | chunk = chunk:sub(pos, pos + n - consumed - 1) 53 | lastChunkPos = pos + #chunk 54 | else 55 | chunksConsumed = i 56 | 57 | if pos > 1 then 58 | chunk = chunk:sub(pos) 59 | end 60 | end 61 | 62 | table.insert(chunks, chunk) 63 | consumed = consumed + #chunk 64 | 65 | if consumed >= n then 66 | break 67 | end 68 | end 69 | 70 | assert(consumed == n) 71 | local result = table.concat(chunks) 72 | assert(#result == n) 73 | 74 | if consume then 75 | self.__pos = lastChunkPos 76 | util.removeShift(self.__chunks, chunksConsumed) 77 | self.__remaining = self.__remaining - consumed 78 | end 79 | 80 | return result 81 | end, 82 | 83 | remaining = function(self) 84 | return self.__remaining 85 | end, 86 | }, 87 | } 88 | 89 | function lib.makeBuffer(initialContents) 90 | return setmetatable({ 91 | __chunks = {initialContents}, 92 | __remaining = initialContents and #initialContents or 0, 93 | __pos = 1, 94 | }, meta) 95 | end 96 | 97 | return lib 98 | -------------------------------------------------------------------------------- /libtls13/src/util/map.lua: -------------------------------------------------------------------------------- 1 | -- Custom hashmaps. 2 | 3 | local lib = {} 4 | 5 | -- Makes a map that projects its keys to a primitive value for hash computation. 6 | function lib.makeProjectionMap(projection, entries) 7 | local meta = { 8 | __index = function(self, k) 9 | local projectedKey = projection(k) 10 | 11 | return rawget(self, projectedKey) 12 | end, 13 | 14 | __newindex = function(self, k, v) 15 | local projectedKey = projection(k) 16 | 17 | rawset(self, projectedKey, v) 18 | end, 19 | } 20 | 21 | local map = setmetatable({}, meta) 22 | 23 | for k, v in pairs(entries or {}) do 24 | map[k] = v 25 | end 26 | 27 | return map 28 | end 29 | 30 | do 31 | local meta = { 32 | __newindex = function(self, k, v) 33 | assert(self[v] == nil, "value is already present in the map") 34 | 35 | rawset(self, k, v) 36 | rawset(self, v, k) 37 | end, 38 | } 39 | 40 | -- Makes a bidirectional map. 41 | function lib.makeBijectiveMap(entries) 42 | local result = setmetatable({}, meta) 43 | 44 | for k, v in pairs(entries or {}) do 45 | result[k] = v 46 | end 47 | 48 | return result 49 | end 50 | end 51 | 52 | return lib 53 | -------------------------------------------------------------------------------- /libtls13/src/x509/attr.lua: -------------------------------------------------------------------------------- 1 | -- Recognized X.5xx attributes. 2 | 3 | local asn = require("tls13.asn") 4 | local oid = require("tls13.asn.oid") 5 | local utilMap = require("tls13.util.map") 6 | 7 | local lib = {} 8 | 9 | lib.recognizedAttributes = utilMap.makeProjectionMap(tostring) 10 | 11 | function lib.makeDirectoryStringAttribute(name) 12 | return { 13 | getName = function() 14 | return name 15 | end, 16 | 17 | parse = function(_, parser, value) 18 | return parser:parseDirectoryString(value) 19 | end, 20 | } 21 | end 22 | 23 | local makeDsAttr = lib.makeDirectoryStringAttribute 24 | 25 | lib.recognizedAttributes[oid.at.commonName] = makeDsAttr("common name") 26 | lib.recognizedAttributes[oid.at.surname] = makeDsAttr("surname") 27 | lib.recognizedAttributes[oid.at.serialNumber] = makeDsAttr("serial number") 28 | lib.recognizedAttributes[oid.at.countryName] = makeDsAttr("country") 29 | lib.recognizedAttributes[oid.at.localityName] = makeDsAttr("locality") 30 | lib.recognizedAttributes[oid.at.stateOrProvinceName] = 31 | makeDsAttr("state or province") 32 | lib.recognizedAttributes[oid.at.organizationName] = makeDsAttr("organization") 33 | lib.recognizedAttributes[oid.at.organizationalUnitName] = 34 | makeDsAttr("organizational unit") 35 | lib.recognizedAttributes[oid.at.title] = makeDsAttr("title") 36 | lib.recognizedAttributes[oid.at.name] = makeDsAttr("name") 37 | lib.recognizedAttributes[oid.at.givenName] = makeDsAttr("given name") 38 | lib.recognizedAttributes[oid.at.initials] = makeDsAttr("initials") 39 | lib.recognizedAttributes[oid.at.generationQualifier] = 40 | makeDsAttr("generation qualifier") 41 | lib.recognizedAttributes[oid.at.dnQualifier] = 42 | makeDsAttr("distinguished name qualifier") 43 | lib.recognizedAttributes[oid.at.pseudonym] = makeDsAttr("pseudonym") 44 | 45 | lib.recognizedAttributes[oid.domainComponent] = { 46 | getName = function() 47 | return "domain component" 48 | end, 49 | 50 | parse = function(self, parser, value) 51 | local value, err = parser:checkTag(value, asn.asnTags.universal.ia5String) 52 | 53 | if not value then 54 | return nil, err 55 | end 56 | 57 | return value[1] 58 | end, 59 | } 60 | 61 | return lib 62 | -------------------------------------------------------------------------------- /libtls13/test/crypto/cipher/aes.lua: -------------------------------------------------------------------------------- 1 | local util = require("tls13.util") 2 | 3 | context("AES tests #crypto #cipher #aes", function() 4 | local aes = require("tls13.crypto.cipher.aes") 5 | 6 | context("AES-128 tests #aes128", function() 7 | local aes128 = aes.aes128 8 | 9 | test("encrypt then decrypt", function() 10 | local plaintext = "\0\1\2\3\4\5\6\7\8\9\x0a\x0b\x0c\x0d\x0e\x0f" 11 | local key = "fedcba9876543210" 12 | assert.are.equal( 13 | util.toHex(plaintext), 14 | util.toHex(aes128:decrypt(aes128:encrypt(plaintext, key), key)) 15 | ) 16 | end) 17 | 18 | test("NIST example vectors", function() 19 | local blocks = { 20 | util.fromHex("6bc1bee22e409f96e93d7e117393172a"), 21 | util.fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"), 22 | util.fromHex("30c81c46a35ce411e5fbc1191a0a52ef"), 23 | util.fromHex("f69f2445df4f9b17ad2b417be66c3710"), 24 | } 25 | local key = util.fromHex("2b7e151628aed2a6abf7158809cf4f3c") 26 | 27 | assert.are.equal( 28 | "3ad77bb40d7a3660a89ecaf32466ef97", 29 | util.toHex(aes128:encrypt(blocks[1], key)) 30 | ) 31 | assert.are.equal( 32 | "f5d3d58503b9699de785895a96fdbaaf", 33 | util.toHex(aes128:encrypt(blocks[2], key)) 34 | ) 35 | assert.are.equal( 36 | "43b1cd7f598ece23881b00e3ed030688", 37 | util.toHex(aes128:encrypt(blocks[3], key)) 38 | ) 39 | assert.are.equal( 40 | "7b0c785e27e8ad3f8223207104725dd4", 41 | util.toHex(aes128:encrypt(blocks[4], key)) 42 | ) 43 | end) 44 | end) 45 | 46 | context("AES-256 tests #aes256", function() 47 | local aes256 = aes.aes256 48 | 49 | test("encrypt then decrypt", function() 50 | local plaintext = "\0\1\2\3\4\5\6\7\8\9\x0a\x0b\x0c\x0d\x0e\x0f" 51 | local key = "fedcba9876543210fedcba9876543210" 52 | assert.are.equal( 53 | util.toHex(plaintext), 54 | util.toHex(aes256:decrypt(aes256:encrypt(plaintext, key), key)) 55 | ) 56 | end) 57 | 58 | test("NIST example vectors", function() 59 | local blocks = { 60 | util.fromHex("6bc1bee22e409f96e93d7e117393172a"), 61 | util.fromHex("ae2d8a571e03ac9c9eb76fac45af8e51"), 62 | util.fromHex("30c81c46a35ce411e5fbc1191a0a52ef"), 63 | util.fromHex("f69f2445df4f9b17ad2b417be66c3710"), 64 | } 65 | local key = util.fromHex( 66 | "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 67 | ) 68 | 69 | assert.are.equal( 70 | "f3eed1bdb5d2a03c064b5a7e3db181f8", 71 | util.toHex(aes256:encrypt(blocks[1], key)) 72 | ) 73 | assert.are.equal( 74 | "591ccb10d410ed26dc5ba74a31362870", 75 | util.toHex(aes256:encrypt(blocks[2], key)) 76 | ) 77 | assert.are.equal( 78 | "b6ed21b99ca6f4f9f153e7b1beafed1d", 79 | util.toHex(aes256:encrypt(blocks[3], key)) 80 | ) 81 | assert.are.equal( 82 | "23304b7a39f9f3ff067d8d8f9e24ecc7", 83 | util.toHex(aes256:encrypt(blocks[4], key)) 84 | ) 85 | end) 86 | end) 87 | end) 88 | -------------------------------------------------------------------------------- /libtls13/test/crypto/hash/sha2.lua: -------------------------------------------------------------------------------- 1 | local util = require("tls13.util") 2 | 3 | context("SHA2-256 tests #crypto #hash #sha2 #sha2-256", function() 4 | local sha2 = require("tls13.crypto.hash.sha2") 5 | 6 | local function hash(message) 7 | local bytes = sha2.sha256() 8 | :update(message) 9 | :finish() 10 | 11 | return util.toHex(bytes) 12 | end 13 | 14 | test("zero-length message", function() 15 | assert.are.equal( 16 | "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 17 | hash("") 18 | ) 19 | end) 20 | 21 | test("hello world", function() 22 | assert.are.equal( 23 | "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", 24 | hash("hello world") 25 | ) 26 | end) 27 | 28 | context("messages from data/sha256.txt", function() 29 | for line in io.lines("./test/data/sha256.txt") do 30 | local inputHex, expectedHash = line:match("(%S+) (%S+)") 31 | local input = util.fromHex(inputHex) 32 | 33 | test(#input .. "-byte input", function() 34 | assert.are.equal(expectedHash, hash(input)) 35 | end) 36 | end 37 | end) 38 | 39 | test("chunked", function() 40 | local hasher = sha2.sha256() 41 | hasher:update(("\1"):rep(30)) 42 | hasher:update(("\2"):rep(41)) 43 | hasher:update("") 44 | hasher:update(("\3"):rep(500)) 45 | 46 | assert.are.equal( 47 | hash(("\1"):rep(30) .. ("\2"):rep(41) .. ("\3"):rep(500)), 48 | util.toHex(hasher:finish()) 49 | ) 50 | end) 51 | end) 52 | 53 | context("SHA2-384 tests #crypto #hash #sha2 #sha2-384", function() 54 | local sha2 = require("tls13.crypto.hash.sha2") 55 | 56 | local function hash(message) 57 | local bytes = sha2.sha384() 58 | :update(message) 59 | :finish() 60 | 61 | return util.toHex(bytes) 62 | end 63 | 64 | test("zero-length message", function() 65 | assert.are.equal( 66 | "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", 67 | hash("") 68 | ) 69 | end) 70 | 71 | test("hello world", function() 72 | assert.are.equal( 73 | "fdbd8e75a67f29f701a4e040385e2e23986303ea10239211af907fcbb83578b3e417cb71ce646efd0819dd8c088de1bd", 74 | hash("hello world") 75 | ) 76 | end) 77 | 78 | context("messages from data/sha384.txt", function() 79 | for line in io.lines("./test/data/sha384.txt") do 80 | local inputHex, expectedHash = line:match("(%S+) (%S+)") 81 | local input = util.fromHex(inputHex) 82 | 83 | test(#input .. "-byte input", function() 84 | assert.are.equal(expectedHash, hash(input)) 85 | end) 86 | end 87 | end) 88 | 89 | test("chunked", function() 90 | local hasher = sha2.sha384() 91 | hasher:update(("\1"):rep(30)) 92 | hasher:update(("\2"):rep(41)) 93 | hasher:update("") 94 | hasher:update(("\3"):rep(500)) 95 | 96 | assert.are.equal( 97 | hash(("\1"):rep(30) .. ("\2"):rep(41) .. ("\3"):rep(500)), 98 | util.toHex(hasher:finish()) 99 | ) 100 | end) 101 | end) 102 | 103 | context("SHA2-512 tests #crypto #hash #sha2 #sha2-512", function() 104 | local sha2 = require("tls13.crypto.hash.sha2") 105 | 106 | local function hash(message) 107 | local bytes = sha2.sha512() 108 | :update(message) 109 | :finish() 110 | 111 | return util.toHex(bytes) 112 | end 113 | 114 | test("zero-length message", function() 115 | assert.are.equal( 116 | "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", 117 | hash("") 118 | ) 119 | end) 120 | 121 | test("hello world", function() 122 | assert.are.equal( 123 | "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f", 124 | hash("hello world") 125 | ) 126 | end) 127 | 128 | context("messages from data/sha512.txt", function() 129 | for line in io.lines("./test/data/sha512.txt") do 130 | local inputHex, expectedHash = line:match("(%S+) (%S+)") 131 | local input = util.fromHex(inputHex) 132 | 133 | test(#input .. "-byte input", function() 134 | assert.are.equal(expectedHash, hash(input)) 135 | end) 136 | end 137 | end) 138 | 139 | test("chunked", function() 140 | local hasher = sha2.sha512() 141 | hasher:update(("\1"):rep(30)) 142 | hasher:update(("\2"):rep(41)) 143 | hasher:update("") 144 | hasher:update(("\3"):rep(500)) 145 | 146 | assert.are.equal( 147 | hash(("\1"):rep(30) .. ("\2"):rep(41) .. ("\3"):rep(500)), 148 | util.toHex(hasher:finish()) 149 | ) 150 | end) 151 | end) 152 | -------------------------------------------------------------------------------- /libtls13/test/crypto/hkdf.lua: -------------------------------------------------------------------------------- 1 | local util = require("tls13.util") 2 | local testUtil = require("test.test-util")(_ENV) 3 | 4 | context("HKDF tests #crypto #hkdf", function() 5 | local hmac = require("crypto.hmac") 6 | local hkdf = require("crypto.hkdf") 7 | local sha2 = require("crypto.hash.sha2") 8 | 9 | local hkdfSha256 = hkdf.hkdf(hmac.hmac(sha2.sha256)) 10 | local hkdfSha384 = hkdf.hkdf(hmac.hmac(sha2.sha384)) 11 | local hkdfSha512 = hkdf.hkdf(hmac.hmac(sha2.sha512)) 12 | 13 | local function check(args) 14 | local hkdf = args.hkdf 15 | local ikm = args.ikm 16 | local salt = args.salt 17 | local context = args.context 18 | local len = args.len 19 | local okm = args.okm 20 | local invalid = args.invalid 21 | 22 | ikm = util.fromHex(ikm) 23 | salt = util.fromHex(salt) 24 | context = util.fromHex(context) 25 | okm = util.fromHex(okm) 26 | 27 | local key = hkdf:extract(ikm, salt) 28 | 29 | if invalid then 30 | assert.has.errors(function() 31 | return hkdf:expand(context, len, key) 32 | end) 33 | else 34 | local actualOkm = hkdf:expand(context, len, key) 35 | assert.are.equal(util.toHex(okm), util.toHex(actualOkm)) 36 | end 37 | end 38 | 39 | context("Project Wycheproof test vectors", function() 40 | local function wycheproofTests(file, hashName, hkdf) 41 | testUtil.makeWycheproofTests { 42 | file = file, 43 | 44 | groupName = function(testGroup) 45 | return ("%s, key size: %d"):format(hashName, testGroup.keySize) 46 | end, 47 | 48 | runTest = function(testSpec, testGroup) 49 | check { 50 | hkdf = hkdf, 51 | ikm = testSpec.ikm, 52 | salt = testSpec.salt, 53 | context = testSpec.info, 54 | len = testSpec.size, 55 | okm = testSpec.okm, 56 | invalid = testSpec.result == "invalid", 57 | } 58 | end, 59 | } 60 | end 61 | 62 | wycheproofTests("hkdf_sha256_test.json", "sha256", hkdfSha256) 63 | wycheproofTests("hkdf_sha384_test.json", "sha384", hkdfSha384) 64 | wycheproofTests("hkdf_sha512_test.json", "sha512", hkdfSha512) 65 | end) 66 | end) 67 | -------------------------------------------------------------------------------- /libtls13/test/crypto/rsa.lua: -------------------------------------------------------------------------------- 1 | local util = require("tls13.util") 2 | 3 | local testUtil = require("test.test-util")(_ENV) 4 | 5 | context("RSA signature tests #crypto #rsa", function() 6 | local oid = require("tls13.asn.oid") 7 | local rsa = require("tls13.crypto.rsa") 8 | local sha2 = require("tls13.crypto.hash.sha2") 9 | 10 | local hashSpecs = { 11 | ["SHA-256"] = { 12 | hash = sha2.sha256, 13 | oid = oid.hashalgs.sha256, 14 | }, 15 | ["SHA-512"] = { 16 | hash = sha2.sha512, 17 | oid = oid.hashalgs.sha512, 18 | }, 19 | } 20 | 21 | context("RSASSA-PKCS1-v1.5 signatures #pkcs1v15sig", function() 22 | context("Project Wycheproof test vectors", function() 23 | testUtil.makeWycheproofTests { 24 | file = "rsa_signature_test.json", 25 | 26 | prepareGroupData = function(testGroup) 27 | return { 28 | hashSpec = hashSpecs[testGroup.sha], 29 | } 30 | end, 31 | 32 | groupFilter = function(testGroup, groupData) 33 | return groupData.hashSpec ~= nil 34 | end, 35 | 36 | groupName = function(testGroup, groupData) 37 | return ("key size: %d, hash algorithm: %s"):format( 38 | testGroup.keysize, 39 | testGroup.sha 40 | ) 41 | end, 42 | 43 | testFilter = function(testSpec) 44 | return testSpec.result ~= "acceptable" 45 | end, 46 | 47 | runTest = function(testSpec, testGroup, groupData) 48 | local hashSpec = groupData.hashSpec 49 | local pkcs1V15 = rsa.rsassaPkcs1V15(hashSpec.hash, hashSpec.oid) 50 | local pubKey = 51 | rsa.makePublicKeyFromHex(testGroup.n, testGroup.e) 52 | local message = util.fromHex(testSpec.msg) 53 | local signature = util.fromHex(testSpec.sig) 54 | 55 | local result, err = pkcs1V15:verify(pubKey, message, signature) 56 | 57 | if testSpec.result == "valid" then 58 | assert.is.Nil(err) 59 | assert.is.True(result) 60 | else 61 | assert.is.falsy(result) 62 | end 63 | end, 64 | } 65 | end) 66 | end) 67 | 68 | context("RSASSA-PSS signatures #psssig", function() 69 | local function wycheproofPssTest(file) 70 | testUtil.makeWycheproofTests { 71 | file = file, 72 | 73 | prepareGroupData = function(testGroup) 74 | return { 75 | hashSpec = hashSpecs[testGroup.sha], 76 | mgfHashSpec = hashSpecs[testGroup.mgfSha], 77 | } 78 | end, 79 | 80 | groupFilter = function(testGroup, groupData) 81 | return 82 | groupData.hashSpec ~= nil 83 | and groupData.mgfHashSpec ~= nil 84 | and testGroup.mgf == "MGF1" 85 | end, 86 | 87 | groupName = function(testGroup, groupData) 88 | return 89 | ("key size: %d, hash: %s, MGF: %s, MGF hash: %s, salt len: %d") 90 | :format( 91 | testGroup.keysize, 92 | testGroup.sha, 93 | testGroup.mgf, 94 | testGroup.mgfSha, 95 | testGroup.sLen 96 | ) 97 | end, 98 | 99 | runTest = function(testSpec, testGroup, groupData) 100 | local hash = groupData.hashSpec.hash 101 | local mgfHash = groupData.mgfHashSpec.hash 102 | local mgf1 = rsa.mgf1(mgfHash) 103 | local pss = rsa.rsassaPss(hash, mgf1, testGroup.sLen) 104 | 105 | local pubKey = 106 | rsa.makePublicKeyFromHex(testGroup.n, testGroup.e) 107 | local message = util.fromHex(testSpec.msg) 108 | local signature = util.fromHex(testSpec.sig) 109 | 110 | if testSpec.result == "valid" then 111 | assert.is.True(pss:verify(pubKey, message, signature)) 112 | else 113 | assert.is.False(pss:verify(pubKey, message, signature)) 114 | end 115 | end, 116 | } 117 | end 118 | 119 | wycheproofPssTest("rsa_pss_2048_sha256_mgf1_0_test.json") 120 | wycheproofPssTest("rsa_pss_2048_sha256_mgf1_32_test.json") 121 | wycheproofPssTest("rsa_pss_2048_sha512_256_mgf1_28_test.json") 122 | wycheproofPssTest("rsa_pss_2048_sha512_256_mgf1_32_test.json") 123 | wycheproofPssTest("rsa_pss_3072_sha256_mgf1_32_test.json") 124 | wycheproofPssTest("rsa_pss_4096_sha256_mgf1_32_test.json") 125 | wycheproofPssTest("rsa_pss_4096_sha512_mgf1_32_test.json") 126 | wycheproofPssTest("rsa_pss_misc_test.json") 127 | end) 128 | end) 129 | -------------------------------------------------------------------------------- /libtls13/test/data/lencr-org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEpzCCA4+gAwIBAgISBCbiG+DYzTHP5EB+Ds6LrJ4LMA0GCSqGSIb3DQEBCwUA 3 | MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD 4 | EwJSMzAeFw0yMzA2MDEyMjIwMjNaFw0yMzA4MzAyMjIwMjJaMBQxEjAQBgNVBAMT 5 | CWxlbmNyLm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNXRvpsYEUs/7yZz 6 | FlLZa8v/loxgzvbM0n0iFAy2y7+1Hq859OhfSNS3KaWJmFoMX2Ad8qYeyT+4+jKj 7 | pLCieBGjggKeMIICmjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH 8 | AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLvZJZCPU4vcD9Cd 9 | CEkukjuA6t9pMB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJQOYfr52LFMLGMFUGCCsG 10 | AQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL3IzLm8ubGVuY3Iub3JnMCIG 11 | CCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5vcmcvMG8GA1UdEQRoMGaCCWxl 12 | bmNyLm9yZ4IPbGV0c2VuY3J5cHQuY29tgg9sZXRzZW5jcnlwdC5vcmeCDXd3dy5s 13 | ZW5jci5vcmeCE3d3dy5sZXRzZW5jcnlwdC5jb22CE3d3dy5sZXRzZW5jcnlwdC5v 14 | cmcwTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEF 15 | BQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQC 16 | BIH0BIHxAO8AdgC3Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmBJe20mQAAAYh5 17 | QyW2AAAEAwBHMEUCIQDSMVHicHd0LWLVkmZ2anWUCkEmzyLVujYHG6wQ7wSIJwIg 18 | czi1R2Nt2YouELUoKFj5MimWz9jxLo82nARTqAbNn98AdQB6MoxU2LcttiDqOOBS 19 | HumEFnAyE4VNO9IrwTpXo1LrUgAAAYh5QyXLAAAEAwBGMEQCIE4hJymj2tqwEIcB 20 | 5ttWb77vtnD9DEiZjhd53wruXxviAiAL9D7xK2X1vIQ/XOTgCaSr7bXsG7ZVQLYv 21 | 1XM1GtSx7zANBgkqhkiG9w0BAQsFAAOCAQEAjDpetclCybO7THL9yMu3dCKfXMFl 22 | R4CI+09ACbzfvHK0QcftYOsWKsroqbHyqoL/hHTeV7j5NPorceXiKLMopQOq/H9l 23 | +DVuksEpwlOk7m1/rzxOYj52rLyWrOy6WthRFW3EB4xo45tU9kzL333v+ll/Bh3t 24 | xqMhQOW5IOZPJEiGCR8MNgkfNe5R71t0C23cMDwoEMuBbAceepUauDhOe+QedRz1 25 | 0p2dBXk4G4iuPWEJkOSk2x5VRfLoTahLtjtTV+Flr722hDpWzlG5jh0Lvub4WNuv 26 | UNW8JxgsElR9c3G7nA+gN7yR62UPOaLTXLzSWVsgb6sOJBPdOKzo1/GVUA== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /libtls13/test/test-util.lua: -------------------------------------------------------------------------------- 1 | return function(_ENV) 2 | local json = dofile("third-party/json.lua/json.lua") 3 | 4 | local lib = {} 5 | 6 | lib.json = json 7 | 8 | function lib.makeWycheproofTests(args) 9 | local f = 10 | assert(io.open("third-party/wycheproof/testvectors/" .. args.file, "r")) 11 | local testJson = f:read("a") 12 | f:close() 13 | local tests = json.decode(testJson) 14 | 15 | for _, testGroup in ipairs(tests.testGroups) do 16 | local groupData = args.prepareGroupData and args.prepareGroupData(testGroup) 17 | 18 | if not args.groupFilter or args.groupFilter(testGroup, groupData) then 19 | context(args.groupName(testGroup, groupData), function() 20 | for _, testSpec in ipairs(testGroup.tests) do 21 | local testData = 22 | args.prepareTestData and args.prepareTestData(testSpec) 23 | 24 | if not args.testFilter or 25 | args.testFilter(testSpec, testGroup, testData, groupData) then 26 | local testName = ("test %d"):format(testSpec.tcId) 27 | 28 | if testSpec.comment ~= "" then 29 | testName = testName .. ": " .. testSpec.comment 30 | end 31 | 32 | test(testName, function() 33 | args.runTest(testSpec, testGroup, groupData, testData) 34 | end) 35 | end 36 | end 37 | end) 38 | end 39 | end 40 | end 41 | 42 | return lib 43 | end 44 | -------------------------------------------------------------------------------- /libtls13/tls13: -------------------------------------------------------------------------------- 1 | src -------------------------------------------------------------------------------- /lua-lockbox/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 James L. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /lua-lockbox/README.md: -------------------------------------------------------------------------------- 1 | *This is not my library, but I need it for my programs. So I've packaged it on my repository.* 2 | 3 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/lua-lockbox). 4 | 5 | [The original repository](https://github.com/somesocks/lua-lockbox). 6 | 7 | # The Lua Lockbox 8 | 9 | [![Build Status](https://travis-ci.org/somesocks/lua-lockbox.svg?branch=master)](https://travis-ci.org/somesocks/lua-lockbox) 10 | 11 | A collection of cryptographic primitives and protocols written in pure Lua. This was written to provide cross-platform, tested reference implementations of many different cryptographic primitives. These are written to be easy to read and easy to use, not for performance! 12 | 13 | # Implemented Primitives 14 | 15 | Digests: 16 | * MD2 17 | * MD4 18 | * MD5 19 | * RIPEMD128 20 | * RIPEMD160 21 | * SHA1 22 | * SHA2-224 23 | * SHA2-256 24 | 25 | Message Authentication Codes (MACs): 26 | * HMAC 27 | 28 | Key Derivation Functions (KDFs): 29 | * PBKDF2 30 | 31 | Block Ciphers: 32 | * DES 33 | * DES3 34 | * AES128 35 | * AES192 36 | * AES256 37 | * TEA 38 | * XTEA 39 | 40 | Block Cipher Modes: 41 | * ECB 42 | * CBC 43 | * PCBC 44 | * CFB 45 | * OFB 46 | * CTR 47 | * IGE 48 | 49 | Block Cipher Padding: 50 | * Zero Padding 51 | * ANSI X.923 Padding 52 | * ISO/IEC 7816 Padding 53 | * PKCS7 Padding (PKCS5-Compatible) 54 | 55 | # Usage 56 | To use these cryptographic primitives in a project, you'll likely have to modify Lockbox.lua to change the module search path. All the primitives import this module to find the packages they require. See RunTests.lua as an example. 57 | 58 | The cryptographic primitives are designed to work on streams of bytes. There are three data structures used to help with this: Array(a Lua array of bytes), Stream(an iterator that returns a series of bytes), and Queue(a FIFO pipe of bytes). See Array.lua, Stream.lua, and Queue.lua for more details. 59 | 60 | Most cryptographic primitives are designed in a builder-style pattern. They usually have three functions: init, update, and finish. All of these functions will return the primitive, so you can chain functions calls together. 61 | 62 | * init() - resets the state of the primitive, so you can reuse it. 63 | * update( byteStream ) - takes in a Stream of bytes, and updates its internal state. This function can be called repeatedly, which effectively concatenates separate inputs. If the primitive requires an IV, it is usually read as the first input provided to update. 64 | * finish() - does any finalization necessary to finish generating output. 65 | 66 | For examples of how to use the different primitives, read the test case files under tests. 67 | 68 | # Security Concerns 69 | Several weak or broken primitives are implemented in this library, for research or legacy reasons. These should not be used under normal circumstances! To restrict their usage, they have been marked as insecure, with the Lockbox.insecure() method. This will cause a failed assertion when you attempt to import the module, unless you set Lockbox.ALLOW_INSECURE to true before the import. For an example, see RunTests.lua. 70 | 71 | # Modules names 72 | 73 | * `lockbox` (or `lockbox.init`) 74 | * `lockbox.cipher.aes128` 75 | * `lockbox.cipher.aes192` 76 | * `lockbox.cipher.aes256` 77 | * `lockbox.cipher.des3` 78 | * `lockbox.cipher.des` 79 | * `lockbox.cipher.mode.cbc` 80 | * `lockbox.cipher.mode.cfb` 81 | * `lockbox.cipher.mode.ctr` 82 | * `lockbox.cipher.mode.ecb` 83 | * `lockbox.cipher.mode.ige` 84 | * `lockbox.cipher.mode.ofb` 85 | * `lockbox.cipher.mode.pcbc` 86 | * `lockbox.digest.md2` 87 | * `lockbox.digest.md4` 88 | * `lockbox.digest.md5` 89 | * `lockbox.digest.ripemd128` 90 | * `lockbox.digest.ripemd160` 91 | * `lockbox.digest.sha1` 92 | * `lockbox.digest.sha2_224` 93 | * `lockbox.digest.sha2_256` 94 | * `lockbox.kdf.pbkdf2` 95 | * `lockbox.mac.hmac` 96 | * `lockbox.padding.ansix923` 97 | * `lockbox.padding.isoiec7816` 98 | * `lockbox.padding.pkcs7` 99 | * `lockbox.padding.zero` 100 | * `lockbox.util.base64` 101 | * `lockbox.util.array` 102 | * `lockbox.util.bit` 103 | * `lockbox.util.queue` 104 | * `lockbox.util.stream` 105 | 106 | # Planned Updates 107 | * RC4 108 | * XXTEA 109 | * SHA3(Keccak) 110 | * MD6 111 | * BLAKE2s 112 | * bcrypt / scrypt 113 | 114 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/des3.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Array = require("lockbox.util.array"); 4 | 5 | local DES = require("lockbox.cipher.des"); 6 | 7 | local DES3 = {}; 8 | 9 | local getKeys = function(keyBlock) 10 | local size = Array.size(keyBlock) 11 | 12 | local key1; 13 | local key2; 14 | local key3; 15 | 16 | if (size == 8) then 17 | key1 = keyBlock; 18 | key2 = keyBlock; 19 | key3 = keyBlock; 20 | elseif (size == 16) then 21 | key1 = Array.slice(keyBlock,1,8); 22 | key2 = Array.slice(keyBlock,9,16); 23 | key3 = key1; 24 | elseif (size == 24) then 25 | key1 = Array.slice(keyBlock,1,8); 26 | key2 = Array.slice(keyBlock,9,16); 27 | key3 = Array.slice(keyBlock,17,24); 28 | else 29 | assert(false,"Invalid key size for 3DES"); 30 | end 31 | 32 | return key1,key2,key3; 33 | end 34 | 35 | DES3.blockSize = DES.blockSize; 36 | 37 | DES3.encrypt = function(keyBlock,inputBlock) 38 | local key1; 39 | local key2; 40 | local key3; 41 | 42 | key1, key2, key3 = getKeys(keyBlock); 43 | 44 | local block = inputBlock; 45 | block = DES.encrypt(key1,block); 46 | block = DES.decrypt(key2,block); 47 | block = DES.encrypt(key3,block); 48 | 49 | return block; 50 | end 51 | 52 | DES3.decrypt = function(keyBlock,inputBlock) 53 | local key1; 54 | local key2; 55 | local key3; 56 | 57 | key1, key2, key3 = getKeys(keyBlock); 58 | 59 | local block = inputBlock; 60 | block = DES.decrypt(key3,block); 61 | block = DES.encrypt(key2,block); 62 | block = DES.decrypt(key1,block); 63 | 64 | return block; 65 | end 66 | 67 | return DES3; 68 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/cbc.lua: -------------------------------------------------------------------------------- 1 | local Array = require("lockbox.util.array"); 2 | local Stream = require("lockbox.util.stream"); 3 | local Queue = require("lockbox.util.queue"); 4 | 5 | local String = require("string"); 6 | local Bit = require("lockbox.util.bit"); 7 | 8 | local CBC = {}; 9 | 10 | CBC.Cipher = function() 11 | 12 | local public = {}; 13 | 14 | local key; 15 | local blockCipher; 16 | local padding; 17 | local inputQueue; 18 | local outputQueue; 19 | local iv; 20 | 21 | public.setKey = function(keyBytes) 22 | key = keyBytes; 23 | return public; 24 | end 25 | 26 | public.setBlockCipher = function(cipher) 27 | blockCipher = cipher; 28 | return public; 29 | end 30 | 31 | public.setPadding = function(paddingMode) 32 | padding = paddingMode; 33 | return public; 34 | end 35 | 36 | public.init = function() 37 | inputQueue = Queue(); 38 | outputQueue = Queue(); 39 | iv = nil; 40 | return public; 41 | end 42 | 43 | public.update = function(messageStream) 44 | local byte = messageStream(); 45 | while (byte ~= nil) do 46 | inputQueue.push(byte); 47 | if(inputQueue.size() >= blockCipher.blockSize) then 48 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 49 | 50 | if(iv == nil) then 51 | iv = block; 52 | else 53 | local out = Array.XOR(iv,block); 54 | out = blockCipher.encrypt(key,out); 55 | Array.writeToQueue(outputQueue,out); 56 | iv = out; 57 | end 58 | end 59 | byte = messageStream(); 60 | end 61 | return public; 62 | end 63 | 64 | public.finish = function() 65 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 66 | public.update(paddingStream); 67 | 68 | return public; 69 | end 70 | 71 | public.getOutputQueue = function() 72 | return outputQueue; 73 | end 74 | 75 | public.asHex = function() 76 | return Stream.toHex(outputQueue.pop); 77 | end 78 | 79 | public.asBytes = function() 80 | return Stream.toArray(outputQueue.pop); 81 | end 82 | 83 | return public; 84 | 85 | end 86 | 87 | 88 | CBC.Decipher = function() 89 | 90 | local public = {}; 91 | 92 | local key; 93 | local blockCipher; 94 | local padding; 95 | local inputQueue; 96 | local outputQueue; 97 | local iv; 98 | 99 | public.setKey = function(keyBytes) 100 | key = keyBytes; 101 | return public; 102 | end 103 | 104 | public.setBlockCipher = function(cipher) 105 | blockCipher = cipher; 106 | return public; 107 | end 108 | 109 | public.setPadding = function(paddingMode) 110 | padding = paddingMode; 111 | return public; 112 | end 113 | 114 | public.init = function() 115 | inputQueue = Queue(); 116 | outputQueue = Queue(); 117 | iv = nil; 118 | return public; 119 | end 120 | 121 | public.update = function(messageStream) 122 | local byte = messageStream(); 123 | while (byte ~= nil) do 124 | inputQueue.push(byte); 125 | if(inputQueue.size() >= blockCipher.blockSize) then 126 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 127 | 128 | if(iv == nil) then 129 | iv = block; 130 | else 131 | local out = block; 132 | out = blockCipher.decrypt(key,out); 133 | out = Array.XOR(iv,out); 134 | Array.writeToQueue(outputQueue,out); 135 | iv = block; 136 | end 137 | end 138 | byte = messageStream(); 139 | end 140 | return public; 141 | end 142 | 143 | public.finish = function() 144 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 145 | public.update(paddingStream); 146 | 147 | return public; 148 | end 149 | 150 | public.getOutputQueue = function() 151 | return outputQueue; 152 | end 153 | 154 | public.asHex = function() 155 | return Stream.toHex(outputQueue.pop); 156 | end 157 | 158 | public.asBytes = function() 159 | return Stream.toArray(outputQueue.pop); 160 | end 161 | 162 | return public; 163 | 164 | end 165 | 166 | return CBC; 167 | 168 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/cfb.lua: -------------------------------------------------------------------------------- 1 | local Array = require("lockbox.util.array"); 2 | local Stream = require("lockbox.util.stream"); 3 | local Queue = require("lockbox.util.queue"); 4 | 5 | local String = require("string"); 6 | local Bit = require("lockbox.util.bit"); 7 | 8 | local CFB = {}; 9 | 10 | CFB.Cipher = function() 11 | 12 | local public = {}; 13 | 14 | local key; 15 | local blockCipher; 16 | local padding; 17 | local inputQueue; 18 | local outputQueue; 19 | local iv; 20 | 21 | public.setKey = function(keyBytes) 22 | key = keyBytes; 23 | return public; 24 | end 25 | 26 | public.setBlockCipher = function(cipher) 27 | blockCipher = cipher; 28 | return public; 29 | end 30 | 31 | public.setPadding = function(paddingMode) 32 | padding = paddingMode; 33 | return public; 34 | end 35 | 36 | public.init = function() 37 | inputQueue = Queue(); 38 | outputQueue = Queue(); 39 | iv = nil; 40 | return public; 41 | end 42 | 43 | public.update = function(messageStream) 44 | local byte = messageStream(); 45 | while (byte ~= nil) do 46 | inputQueue.push(byte); 47 | if(inputQueue.size() >= blockCipher.blockSize) then 48 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 49 | 50 | if(iv == nil) then 51 | iv = block; 52 | else 53 | local out = iv; 54 | out = blockCipher.encrypt(key,out); 55 | out = Array.XOR(out,block); 56 | Array.writeToQueue(outputQueue,out); 57 | iv = out; 58 | end 59 | end 60 | byte = messageStream(); 61 | end 62 | return public; 63 | end 64 | 65 | public.finish = function() 66 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 67 | public.update(paddingStream); 68 | 69 | return public; 70 | end 71 | 72 | public.getOutputQueue = function() 73 | return outputQueue; 74 | end 75 | 76 | public.asHex = function() 77 | return Stream.toHex(outputQueue.pop); 78 | end 79 | 80 | public.asBytes = function() 81 | return Stream.toArray(outputQueue.pop); 82 | end 83 | 84 | return public; 85 | 86 | end 87 | 88 | CFB.Decipher = function() 89 | 90 | local public = {}; 91 | 92 | local key; 93 | local blockCipher; 94 | local padding; 95 | local inputQueue; 96 | local outputQueue; 97 | local iv; 98 | 99 | public.setKey = function(keyBytes) 100 | key = keyBytes; 101 | return public; 102 | end 103 | 104 | public.setBlockCipher = function(cipher) 105 | blockCipher = cipher; 106 | return public; 107 | end 108 | 109 | public.setPadding = function(paddingMode) 110 | padding = paddingMode; 111 | return public; 112 | end 113 | 114 | public.init = function() 115 | inputQueue = Queue(); 116 | outputQueue = Queue(); 117 | iv = nil; 118 | return public; 119 | end 120 | 121 | public.update = function(messageStream) 122 | local byte = messageStream(); 123 | while (byte ~= nil) do 124 | inputQueue.push(byte); 125 | if(inputQueue.size() >= blockCipher.blockSize) then 126 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 127 | 128 | if(iv == nil) then 129 | iv = block; 130 | else 131 | local out = iv; 132 | out = blockCipher.encrypt(key,out); 133 | out = Array.XOR(out,block); 134 | Array.writeToQueue(outputQueue,out); 135 | iv = block; 136 | end 137 | end 138 | byte = messageStream(); 139 | end 140 | return public; 141 | end 142 | 143 | public.finish = function() 144 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 145 | public.update(paddingStream); 146 | 147 | return public; 148 | end 149 | 150 | public.getOutputQueue = function() 151 | return outputQueue; 152 | end 153 | 154 | public.asHex = function() 155 | return Stream.toHex(outputQueue.pop); 156 | end 157 | 158 | public.asBytes = function() 159 | return Stream.toArray(outputQueue.pop); 160 | end 161 | 162 | return public; 163 | 164 | end 165 | 166 | return CFB; 167 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/ecb.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Array = require("lockbox.util.array"); 4 | local Stream = require("lockbox.util.stream"); 5 | local Queue = require("lockbox.util.queue"); 6 | 7 | local String = require("string"); 8 | local Bit = require("lockbox.util.bit"); 9 | 10 | local ECB = {}; 11 | 12 | ECB.Cipher = function() 13 | 14 | local public = {}; 15 | 16 | local key; 17 | local blockCipher; 18 | local padding; 19 | local inputQueue; 20 | local outputQueue; 21 | 22 | public.setKey = function(keyBytes) 23 | key = keyBytes; 24 | return public; 25 | end 26 | 27 | public.setBlockCipher = function(cipher) 28 | blockCipher = cipher; 29 | return public; 30 | end 31 | 32 | public.setPadding = function(paddingMode) 33 | padding = paddingMode; 34 | return public; 35 | end 36 | 37 | public.init = function() 38 | inputQueue = Queue(); 39 | outputQueue = Queue(); 40 | return public; 41 | end 42 | 43 | public.update = function(messageStream) 44 | local byte = messageStream(); 45 | while (byte ~= nil) do 46 | inputQueue.push(byte); 47 | if(inputQueue.size() >= blockCipher.blockSize) then 48 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 49 | 50 | block = blockCipher.encrypt(key,block); 51 | 52 | Array.writeToQueue(outputQueue,block); 53 | end 54 | byte = messageStream(); 55 | end 56 | return public; 57 | end 58 | 59 | public.finish = function() 60 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 61 | public.update(paddingStream); 62 | 63 | return public; 64 | end 65 | 66 | public.getOutputQueue = function() 67 | return outputQueue; 68 | end 69 | 70 | public.asHex = function() 71 | return Stream.toHex(outputQueue.pop); 72 | end 73 | 74 | public.asBytes = function() 75 | return Stream.toArray(outputQueue.pop); 76 | end 77 | 78 | return public; 79 | 80 | end 81 | 82 | ECB.Decipher = function() 83 | 84 | local public = {}; 85 | 86 | local key; 87 | local blockCipher; 88 | local padding; 89 | local inputQueue; 90 | local outputQueue; 91 | 92 | public.setKey = function(keyBytes) 93 | key = keyBytes; 94 | return public; 95 | end 96 | 97 | public.setBlockCipher = function(cipher) 98 | blockCipher = cipher; 99 | return public; 100 | end 101 | 102 | public.setPadding = function(paddingMode) 103 | padding = paddingMode; 104 | return public; 105 | end 106 | 107 | public.init = function() 108 | inputQueue = Queue(); 109 | outputQueue = Queue(); 110 | return public; 111 | end 112 | 113 | public.update = function(messageStream) 114 | local byte = messageStream(); 115 | while (byte ~= nil) do 116 | inputQueue.push(byte); 117 | if(inputQueue.size() >= blockCipher.blockSize) then 118 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 119 | 120 | block = blockCipher.decrypt(key,block); 121 | 122 | Array.writeToQueue(outputQueue,block); 123 | end 124 | byte = messageStream(); 125 | end 126 | return public; 127 | end 128 | 129 | public.finish = function() 130 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 131 | public.update(paddingStream); 132 | 133 | return public; 134 | end 135 | 136 | public.getOutputQueue = function() 137 | return outputQueue; 138 | end 139 | 140 | public.asHex = function() 141 | return Stream.toHex(outputQueue.pop); 142 | end 143 | 144 | public.asBytes = function() 145 | return Stream.toArray(outputQueue.pop); 146 | end 147 | 148 | return public; 149 | 150 | end 151 | 152 | 153 | return ECB; 154 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/ige.lua: -------------------------------------------------------------------------------- 1 | local Array = require("lockbox.util.array"); 2 | local Stream = require("lockbox.util.stream"); 3 | local Queue = require("lockbox.util.queue"); 4 | 5 | local String = require("string"); 6 | local Bit = require("lockbox.util.bit"); 7 | 8 | local IGE = {}; 9 | 10 | IGE.Cipher = function() 11 | 12 | local public = {}; 13 | 14 | local key; 15 | local blockCipher; 16 | local padding; 17 | local inputQueue; 18 | local outputQueue; 19 | local xPrev,yPrev; 20 | 21 | public.setKey = function(keyBytes) 22 | key = keyBytes; 23 | return public; 24 | end 25 | 26 | public.setBlockCipher = function(cipher) 27 | blockCipher = cipher; 28 | return public; 29 | end 30 | 31 | public.setPadding = function(paddingMode) 32 | padding = paddingMode; 33 | return public; 34 | end 35 | 36 | public.init = function() 37 | inputQueue = Queue(); 38 | outputQueue = Queue(); 39 | xPrev = nil; 40 | yPrev = nil; 41 | return public; 42 | end 43 | 44 | public.update = function(messageStream) 45 | local byte = messageStream(); 46 | while (byte ~= nil) do 47 | inputQueue.push(byte); 48 | if(inputQueue.size() >= blockCipher.blockSize) then 49 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 50 | 51 | if(yPrev == nil) then 52 | yPrev = block; 53 | elseif(xPrev == nil) then 54 | xPrev = block 55 | else 56 | local out = Array.XOR(yPrev,block); 57 | out = blockCipher.encrypt(key,out); 58 | out = Array.XOR(out,xPrev); 59 | Array.writeToQueue(outputQueue,out); 60 | xPrev = block; 61 | yPrev = out; 62 | end 63 | end 64 | byte = messageStream(); 65 | end 66 | return public; 67 | end 68 | 69 | public.finish = function() 70 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 71 | public.update(paddingStream); 72 | 73 | return public; 74 | end 75 | 76 | public.getOutputQueue = function() 77 | return outputQueue; 78 | end 79 | 80 | public.asHex = function() 81 | return Stream.toHex(outputQueue.pop); 82 | end 83 | 84 | public.asBytes = function() 85 | return Stream.toArray(outputQueue.pop); 86 | end 87 | 88 | return public; 89 | 90 | end 91 | 92 | IGE.Decipher = function() 93 | 94 | local public = {}; 95 | 96 | local key; 97 | local blockCipher; 98 | local padding; 99 | local inputQueue; 100 | local outputQueue; 101 | local xPrev,yPrev; 102 | 103 | public.setKey = function(keyBytes) 104 | key = keyBytes; 105 | return public; 106 | end 107 | 108 | public.setBlockCipher = function(cipher) 109 | blockCipher = cipher; 110 | return public; 111 | end 112 | 113 | public.setPadding = function(paddingMode) 114 | padding = paddingMode; 115 | return public; 116 | end 117 | 118 | public.init = function() 119 | inputQueue = Queue(); 120 | outputQueue = Queue(); 121 | xPrev = nil; 122 | yPrev = nil; 123 | return public; 124 | end 125 | 126 | public.update = function(messageStream) 127 | local byte = messageStream(); 128 | while (byte ~= nil) do 129 | inputQueue.push(byte); 130 | if(inputQueue.size() >= blockCipher.blockSize) then 131 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 132 | 133 | if(xPrev == nil) then 134 | xPrev = block; 135 | elseif(yPrev == nil) then 136 | yPrev = block 137 | else 138 | local out = Array.XOR(yPrev,block); 139 | out = blockCipher.decrypt(key,out); 140 | out = Array.XOR(out,xPrev); 141 | Array.writeToQueue(outputQueue,out); 142 | xPrev = block; 143 | yPrev = out; 144 | end 145 | end 146 | byte = messageStream(); 147 | end 148 | return public; 149 | end 150 | 151 | public.finish = function() 152 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 153 | public.update(paddingStream); 154 | 155 | return public; 156 | end 157 | 158 | public.getOutputQueue = function() 159 | return outputQueue; 160 | end 161 | 162 | public.asHex = function() 163 | return Stream.toHex(outputQueue.pop); 164 | end 165 | 166 | public.asBytes = function() 167 | return Stream.toArray(outputQueue.pop); 168 | end 169 | 170 | return public; 171 | 172 | end 173 | 174 | return IGE; 175 | 176 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/ofb.lua: -------------------------------------------------------------------------------- 1 | local Array = require("lockbox.util.array"); 2 | local Stream = require("lockbox.util.stream"); 3 | local Queue = require("lockbox.util.queue"); 4 | 5 | local String = require("string"); 6 | local Bit = require("lockbox.util.bit"); 7 | 8 | local OFB = {}; 9 | 10 | OFB.Cipher = function() 11 | 12 | local public = {}; 13 | 14 | local key; 15 | local blockCipher; 16 | local padding; 17 | local inputQueue; 18 | local outputQueue; 19 | local iv; 20 | 21 | public.setKey = function(keyBytes) 22 | key = keyBytes; 23 | return public; 24 | end 25 | 26 | public.setBlockCipher = function(cipher) 27 | blockCipher = cipher; 28 | return public; 29 | end 30 | 31 | public.setPadding = function(paddingMode) 32 | padding = paddingMode; 33 | return public; 34 | end 35 | 36 | public.init = function() 37 | inputQueue = Queue(); 38 | outputQueue = Queue(); 39 | iv = nil; 40 | return public; 41 | end 42 | 43 | public.update = function(messageStream) 44 | local byte = messageStream(); 45 | while (byte ~= nil) do 46 | inputQueue.push(byte); 47 | if(inputQueue.size() >= blockCipher.blockSize) then 48 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 49 | 50 | if(iv == nil) then 51 | iv = block; 52 | else 53 | local out = iv; 54 | out = blockCipher.encrypt(key,out); 55 | iv = out; 56 | out = Array.XOR(out,block); 57 | Array.writeToQueue(outputQueue,out); 58 | end 59 | end 60 | byte = messageStream(); 61 | end 62 | return public; 63 | end 64 | 65 | public.finish = function() 66 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 67 | public.update(paddingStream); 68 | 69 | return public; 70 | end 71 | 72 | public.getOutputQueue = function() 73 | return outputQueue; 74 | end 75 | 76 | public.asHex = function() 77 | return Stream.toHex(outputQueue.pop); 78 | end 79 | 80 | public.asBytes = function() 81 | return Stream.toArray(outputQueue.pop); 82 | end 83 | 84 | return public; 85 | 86 | end 87 | 88 | OFB.Decipher = function() 89 | 90 | local public = {}; 91 | 92 | local key; 93 | local blockCipher; 94 | local padding; 95 | local inputQueue; 96 | local outputQueue; 97 | local iv; 98 | 99 | public.setKey = function(keyBytes) 100 | key = keyBytes; 101 | return public; 102 | end 103 | 104 | public.setBlockCipher = function(cipher) 105 | blockCipher = cipher; 106 | return public; 107 | end 108 | 109 | public.setPadding = function(paddingMode) 110 | padding = paddingMode; 111 | return public; 112 | end 113 | 114 | public.init = function() 115 | inputQueue = Queue(); 116 | outputQueue = Queue(); 117 | iv = nil; 118 | return public; 119 | end 120 | 121 | public.update = function(messageStream) 122 | local byte = messageStream(); 123 | while (byte ~= nil) do 124 | inputQueue.push(byte); 125 | if(inputQueue.size() >= blockCipher.blockSize) then 126 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 127 | 128 | if(iv == nil) then 129 | iv = block; 130 | else 131 | local out = iv; 132 | out = blockCipher.encrypt(key,out); 133 | iv = out; 134 | out = Array.XOR(out,block); 135 | Array.writeToQueue(outputQueue,out); 136 | end 137 | end 138 | byte = messageStream(); 139 | end 140 | return public; 141 | end 142 | 143 | public.finish = function() 144 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 145 | public.update(paddingStream); 146 | 147 | return public; 148 | end 149 | 150 | public.getOutputQueue = function() 151 | return outputQueue; 152 | end 153 | 154 | public.asHex = function() 155 | return Stream.toHex(outputQueue.pop); 156 | end 157 | 158 | public.asBytes = function() 159 | return Stream.toArray(outputQueue.pop); 160 | end 161 | 162 | return public; 163 | 164 | end 165 | 166 | 167 | return OFB; 168 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/mode/pcbc.lua: -------------------------------------------------------------------------------- 1 | local Array = require("lockbox.util.array"); 2 | local Stream = require("lockbox.util.stream"); 3 | local Queue = require("lockbox.util.queue"); 4 | 5 | local String = require("string"); 6 | local Bit = require("lockbox.util.bit"); 7 | 8 | local PCBC = {}; 9 | 10 | PCBC.Cipher = function() 11 | 12 | local public = {}; 13 | 14 | local key; 15 | local blockCipher; 16 | local padding; 17 | local inputQueue; 18 | local outputQueue; 19 | local iv; 20 | 21 | public.setKey = function(keyBytes) 22 | key = keyBytes; 23 | return public; 24 | end 25 | 26 | public.setBlockCipher = function(cipher) 27 | blockCipher = cipher; 28 | return public; 29 | end 30 | 31 | public.setPadding = function(paddingMode) 32 | padding = paddingMode; 33 | return public; 34 | end 35 | 36 | public.init = function() 37 | inputQueue = Queue(); 38 | outputQueue = Queue(); 39 | iv = nil; 40 | return public; 41 | end 42 | 43 | public.update = function(messageStream) 44 | local byte = messageStream(); 45 | while (byte ~= nil) do 46 | inputQueue.push(byte); 47 | if(inputQueue.size() >= blockCipher.blockSize) then 48 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 49 | 50 | if(iv == nil) then 51 | iv = block; 52 | else 53 | local out = block; 54 | out = Array.XOR(iv,out); 55 | out = blockCipher.encrypt(key,out); 56 | iv = Array.XOR(out,block); 57 | Array.writeToQueue(outputQueue,out); 58 | end 59 | end 60 | byte = messageStream(); 61 | end 62 | return public; 63 | end 64 | 65 | public.finish = function() 66 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 67 | public.update(paddingStream); 68 | 69 | return public; 70 | end 71 | 72 | public.getOutputQueue = function() 73 | return outputQueue; 74 | end 75 | 76 | public.asHex = function() 77 | return Stream.toHex(outputQueue.pop); 78 | end 79 | 80 | public.asBytes = function() 81 | return Stream.toArray(outputQueue.pop); 82 | end 83 | 84 | return public; 85 | 86 | end 87 | 88 | PCBC.Decipher = function() 89 | 90 | local public = {}; 91 | 92 | local key; 93 | local blockCipher; 94 | local padding; 95 | local inputQueue; 96 | local outputQueue; 97 | local iv; 98 | 99 | public.setKey = function(keyBytes) 100 | key = keyBytes; 101 | return public; 102 | end 103 | 104 | public.setBlockCipher = function(cipher) 105 | blockCipher = cipher; 106 | return public; 107 | end 108 | 109 | public.setPadding = function(paddingMode) 110 | padding = paddingMode; 111 | return public; 112 | end 113 | 114 | public.init = function() 115 | inputQueue = Queue(); 116 | outputQueue = Queue(); 117 | iv = nil; 118 | return public; 119 | end 120 | 121 | public.update = function(messageStream) 122 | local byte = messageStream(); 123 | while (byte ~= nil) do 124 | inputQueue.push(byte); 125 | if(inputQueue.size() >= blockCipher.blockSize) then 126 | local block = Array.readFromQueue(inputQueue,blockCipher.blockSize); 127 | 128 | if(iv == nil) then 129 | iv = block; 130 | else 131 | local out = block; 132 | out = blockCipher.decrypt(key,out); 133 | out = Array.XOR(iv,out); 134 | Array.writeToQueue(outputQueue,out); 135 | iv = Array.XOR(out,block); 136 | end 137 | end 138 | byte = messageStream(); 139 | end 140 | return public; 141 | end 142 | 143 | public.finish = function() 144 | paddingStream = padding(blockCipher.blockSize,inputQueue.getHead()); 145 | public.update(paddingStream); 146 | 147 | return public; 148 | end 149 | 150 | public.getOutputQueue = function() 151 | return outputQueue; 152 | end 153 | 154 | public.asHex = function() 155 | return Stream.toHex(outputQueue.pop); 156 | end 157 | 158 | public.asBytes = function() 159 | return Stream.toArray(outputQueue.pop); 160 | end 161 | 162 | return public; 163 | 164 | end 165 | 166 | 167 | return PCBC; 168 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/tea.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Stream = require("lockbox.util.stream"); 4 | local Array = require("lockbox.util.array"); 5 | 6 | local String = require("string"); 7 | local Bit = require("lockbox.util.bit"); 8 | local Math = require("math"); 9 | 10 | local AND = Bit.band; 11 | local OR = Bit.bor; 12 | local NOT = Bit.bnot; 13 | local XOR = Bit.bxor; 14 | local LROT = Bit.lrotate; 15 | local RROT = Bit.rrotate; 16 | local LSHIFT = Bit.lshift; 17 | local RSHIFT = Bit.rshift; 18 | 19 | 20 | --NOTE: TEA is endian-dependent! 21 | --The spec does not seem to specify which to use. 22 | --It looks like most implementations use big-endian 23 | local bytes2word = function(b0,b1,b2,b3) 24 | local i = b0; i = LSHIFT(i,8); 25 | i = OR(i,b1); i = LSHIFT(i,8); 26 | i = OR(i,b2); i = LSHIFT(i,8); 27 | i = OR(i,b3); 28 | return i; 29 | end 30 | 31 | local word2bytes = function(word) 32 | local b0,b1,b2,b3; 33 | b3 = AND(word,0xFF); word = RSHIFT(word,8); 34 | b2 = AND(word,0xFF); word = RSHIFT(word,8); 35 | b1 = AND(word,0xFF); word = RSHIFT(word,8); 36 | b0 = AND(word,0xFF); 37 | return b0,b1,b2,b3; 38 | end 39 | 40 | local TEA = {}; 41 | 42 | TEA.blockSize = 8; 43 | 44 | TEA.encrypt = function(key,data) 45 | local y = bytes2word(data[1],data[2],data[3],data[4]); 46 | local z = bytes2word(data[5],data[6],data[7],data[8]); 47 | local delta = 0x9e3779b9; 48 | local sum = 0; 49 | 50 | local k0 = bytes2word(key[ 1],key[ 2],key[ 3],key[ 4]); 51 | local k1 = bytes2word(key[ 5],key[ 6],key[ 7],key[ 8]); 52 | local k2 = bytes2word(key[ 9],key[10],key[11],key[12]); 53 | local k3 = bytes2word(key[13],key[14],key[15],key[16]); 54 | 55 | for i = 1,32 do 56 | local temp; 57 | 58 | sum = AND(sum + delta, 0xFFFFFFFF); 59 | 60 | temp = z+sum; 61 | temp = XOR(temp,LSHIFT(z,4)+k0); 62 | temp = XOR(temp,RSHIFT(z,5)+k1); 63 | y = AND(y + temp, 0xFFFFFFFF); 64 | 65 | temp = y+sum; 66 | temp = XOR(temp,LSHIFT(y,4)+k2); 67 | temp = XOR(temp,RSHIFT(y,5)+k3); 68 | z = AND( z + temp, 0xFFFFFFFF); 69 | end 70 | 71 | local out = {}; 72 | 73 | out[1],out[2],out[3],out[4] = word2bytes(y); 74 | out[5],out[6],out[7],out[8] = word2bytes(z); 75 | 76 | return out; 77 | end 78 | 79 | TEA.decrypt = function(key,data) 80 | local y = bytes2word(data[1],data[2],data[3],data[4]); 81 | local z = bytes2word(data[5],data[6],data[7],data[8]); 82 | 83 | local delta = 0x9e3779b9; 84 | local sum = 0xc6ef3720; --AND(delta*32,0xFFFFFFFF); 85 | 86 | local k0 = bytes2word(key[ 1],key[ 2],key[ 3],key[ 4]); 87 | local k1 = bytes2word(key[ 5],key[ 6],key[ 7],key[ 8]); 88 | local k2 = bytes2word(key[ 9],key[10],key[11],key[12]); 89 | local k3 = bytes2word(key[13],key[14],key[15],key[16]); 90 | 91 | for i = 1,32 do 92 | local temp; 93 | 94 | temp = y+sum; 95 | temp = XOR(temp,LSHIFT(y,4)+k2); 96 | temp = XOR(temp,RSHIFT(y,5)+k3); 97 | z = AND(z + 0x100000000 - temp,0xFFFFFFFF); 98 | 99 | temp = z+sum; 100 | temp = XOR(temp,LSHIFT(z,4)+k0); 101 | temp = XOR(temp,RSHIFT(z,5)+k1); 102 | y = AND(y + 0x100000000 - temp,0xFFFFFFFF); 103 | 104 | sum = AND(sum + 0x100000000 - delta,0xFFFFFFFF); 105 | end 106 | 107 | local out = {}; 108 | 109 | out[1],out[2],out[3],out[4] = word2bytes(y); 110 | out[5],out[6],out[7],out[8] = word2bytes(z); 111 | 112 | return out; 113 | end 114 | 115 | return TEA; 116 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/cipher/xtea.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Stream = require("lockbox.util.stream"); 4 | local Array = require("lockbox.util.array"); 5 | 6 | local String = require("string"); 7 | local Bit = require("lockbox.util.bit"); 8 | local Math = require("math"); 9 | 10 | local AND = Bit.band; 11 | local OR = Bit.bor; 12 | local NOT = Bit.bnot; 13 | local XOR = Bit.bxor; 14 | local LROT = Bit.lrotate; 15 | local RROT = Bit.rrotate; 16 | local LSHIFT = Bit.lshift; 17 | local RSHIFT = Bit.rshift; 18 | 19 | 20 | --NOTE: XTEA is endian-dependent! 21 | --The spec does not seem to specify which to use. 22 | --It looks like most implementations use big-endian 23 | local bytes2word = function(b0,b1,b2,b3) 24 | local i = b0; i = LSHIFT(i,8); 25 | i = OR(i,b1); i = LSHIFT(i,8); 26 | i = OR(i,b2); i = LSHIFT(i,8); 27 | i = OR(i,b3); 28 | return i; 29 | end 30 | 31 | local word2bytes = function(word) 32 | local b0,b1,b2,b3; 33 | b3 = AND(word,0xFF); word = RSHIFT(word,8); 34 | b2 = AND(word,0xFF); word = RSHIFT(word,8); 35 | b1 = AND(word,0xFF); word = RSHIFT(word,8); 36 | b0 = AND(word,0xFF); 37 | return b0,b1,b2,b3; 38 | end 39 | 40 | local XTEA = {}; 41 | 42 | XTEA.blockSize = 8; 43 | 44 | XTEA.encrypt = function(key,data) 45 | local y = bytes2word(data[1],data[2],data[3],data[4]); 46 | local z = bytes2word(data[5],data[6],data[7],data[8]); 47 | local delta = 0x9e3779b9; 48 | local sum = 0; 49 | 50 | local k0 = bytes2word(key[ 1],key[ 2],key[ 3],key[ 4]); 51 | local k1 = bytes2word(key[ 5],key[ 6],key[ 7],key[ 8]); 52 | local k2 = bytes2word(key[ 9],key[10],key[11],key[12]); 53 | local k3 = bytes2word(key[13],key[14],key[15],key[16]); 54 | local k = {[0] = k0, k1, k2, k3}; 55 | 56 | for i = 1,32 do 57 | local temp; 58 | 59 | temp = XOR(LSHIFT(z,4),RSHIFT(z,5)) + z 60 | temp = XOR(temp,sum + k[ AND(sum,0x3) ]) 61 | y = AND(y + temp, 0xFFFFFFFF); 62 | 63 | sum = AND(sum + delta, 0xFFFFFFFF); 64 | 65 | temp = XOR(LSHIFT(y,4),RSHIFT(y,5)) + y 66 | temp = XOR(temp,sum + k[ AND(RSHIFT(sum,11),0x3) ]) 67 | z = AND( z + temp, 0xFFFFFFFF); 68 | end 69 | 70 | local out = {}; 71 | 72 | out[1],out[2],out[3],out[4] = word2bytes(y); 73 | out[5],out[6],out[7],out[8] = word2bytes(z); 74 | 75 | return out; 76 | end 77 | 78 | XTEA.decrypt = function(key,data) 79 | local y = bytes2word(data[1],data[2],data[3],data[4]); 80 | local z = bytes2word(data[5],data[6],data[7],data[8]); 81 | 82 | local delta = 0x9e3779b9; 83 | local sum = 0xc6ef3720; --AND(delta*32,0xFFFFFFFF); 84 | 85 | local k0 = bytes2word(key[ 1],key[ 2],key[ 3],key[ 4]); 86 | local k1 = bytes2word(key[ 5],key[ 6],key[ 7],key[ 8]); 87 | local k2 = bytes2word(key[ 9],key[10],key[11],key[12]); 88 | local k3 = bytes2word(key[13],key[14],key[15],key[16]); 89 | local k = {[0] = k0, k1, k2, k3}; 90 | 91 | for i = 1,32 do 92 | local temp; 93 | 94 | temp = XOR(LSHIFT(y,4),RSHIFT(y,5)) + y 95 | temp = XOR(temp,sum + k[ AND(RSHIFT(sum,11),0x3) ]) 96 | z = AND(z + 0x100000000 - temp,0xFFFFFFFF); 97 | 98 | sum = AND(sum + 0x100000000 - delta,0xFFFFFFFF); 99 | 100 | temp = XOR(LSHIFT(z,4),RSHIFT(z,5)) + z 101 | temp = XOR(temp,sum + k[ AND(sum,0x3) ]) 102 | y = AND(y + 0x100000000 - temp,0xFFFFFFFF); 103 | 104 | end 105 | 106 | local out = {}; 107 | 108 | out[1],out[2],out[3],out[4] = word2bytes(y); 109 | out[5],out[6],out[7],out[8] = word2bytes(z); 110 | 111 | return out; 112 | end 113 | 114 | return XTEA; 115 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/digest/md2.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Bit = require("lockbox.util.bit"); 4 | local String = require("string"); 5 | local Queue = require("lockbox.util.queue"); 6 | 7 | local SUBST = { 8 | 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 9 | 0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA, 10 | 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 11 | 0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A, 12 | 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, 13 | 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 14 | 0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6, 15 | 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 16 | 0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02, 17 | 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, 18 | 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 19 | 0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52, 20 | 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 21 | 0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39, 22 | 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, 23 | 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 }; 24 | 25 | local AND = Bit.band; 26 | local OR = Bit.bor; 27 | local NOT = Bit.bnot; 28 | local XOR = Bit.bxor; 29 | local LROT = Bit.lrotate; 30 | local RROT = Bit.rrotate; 31 | local LSHIFT = Bit.lshift; 32 | local RSHIFT = Bit.rshift; 33 | 34 | local MD2 = function() 35 | 36 | local queue = Queue(); 37 | 38 | local X = {}; 39 | for i=0,47 do 40 | X[i] = 0x00; 41 | end 42 | 43 | local L = 0; 44 | local C = {}; 45 | for i=0,15 do 46 | C[i] = 0x00; 47 | end 48 | 49 | local public = {}; 50 | 51 | local processBlock = function() 52 | local block = {}; 53 | 54 | for i=0,15 do 55 | block[i] = queue.pop(); 56 | end 57 | 58 | for i=0,15 do 59 | X[i+16] = block[i]; 60 | X[i+32] = XOR(X[i],block[i]); --mix 61 | end 62 | 63 | local t; 64 | 65 | --update block 66 | t=0; 67 | for i=0,17 do 68 | for j=0,47 do 69 | X[j] = XOR(X[j],SUBST[t+1]); 70 | t = X[j]; 71 | end 72 | t = (t+i) % 256; 73 | end 74 | 75 | --update checksum 76 | t = C[15]; 77 | for i=0,15 do 78 | C[i] = XOR(C[i],SUBST[XOR(block[i],t)+1]); 79 | t = C[i]; 80 | end 81 | 82 | end 83 | 84 | public.init = function() 85 | queue.reset(); 86 | 87 | X = {}; 88 | for i=0,47 do 89 | X[i] = 0x00; 90 | end 91 | 92 | L = 0; 93 | C = {}; 94 | for i=0,15 do 95 | C[i] = 0x00; 96 | end 97 | 98 | return public; 99 | end 100 | 101 | public.update = function(stream) 102 | for b in stream do 103 | queue.push(b); 104 | if(queue.size() >= 16) then processBlock(); end 105 | end 106 | 107 | return public; 108 | end 109 | 110 | public.finish = function() 111 | local i = 16-queue.size(); 112 | 113 | while queue.size() < 16 do 114 | queue.push(i); 115 | end 116 | 117 | processBlock(); 118 | 119 | queue.push(C[ 0]); queue.push(C[ 1]); queue.push(C[ 2]); queue.push(C[ 3]); 120 | queue.push(C[ 4]); queue.push(C[ 5]); queue.push(C[ 6]); queue.push(C[ 7]); 121 | queue.push(C[ 8]); queue.push(C[ 9]); queue.push(C[10]); queue.push(C[11]); 122 | queue.push(C[12]); queue.push(C[13]); queue.push(C[14]); queue.push(C[15]); 123 | 124 | processBlock(); 125 | 126 | return public; 127 | end 128 | 129 | public.asBytes = function() 130 | return {X[ 0],X[ 1],X[ 2],X[ 3],X[ 4],X[ 5],X[ 6],X[ 7], 131 | X[ 8],X[ 9],X[10],X[11],X[12],X[13],X[14],X[15]}; 132 | end 133 | 134 | public.asHex = function() 135 | return String.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 136 | X[ 0],X[ 1],X[ 2],X[ 3],X[ 4],X[ 5],X[ 6],X[ 7], 137 | X[ 8],X[ 9],X[10],X[11],X[12],X[13],X[14],X[15]); 138 | end 139 | 140 | return public; 141 | 142 | end 143 | 144 | return MD2; 145 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/digest/sha1.lua: -------------------------------------------------------------------------------- 1 | require("lockbox").insecure(); 2 | 3 | local Bit = require("lockbox.util.bit"); 4 | local String = require("string"); 5 | local Math = require("math"); 6 | local Queue = require("lockbox.util.queue"); 7 | 8 | local AND = Bit.band; 9 | local OR = Bit.bor; 10 | local NOT = Bit.bnot; 11 | local XOR = Bit.bxor; 12 | local LROT = Bit.lrotate; 13 | local RROT = Bit.rrotate; 14 | local LSHIFT = Bit.lshift; 15 | local RSHIFT = Bit.rshift; 16 | 17 | --SHA1 is big-endian 18 | local bytes2word = function(b0,b1,b2,b3) 19 | local i = b0; i = LSHIFT(i,8); 20 | i = OR(i,b1); i = LSHIFT(i,8); 21 | i = OR(i,b2); i = LSHIFT(i,8); 22 | i = OR(i,b3); 23 | return i; 24 | end 25 | 26 | local word2bytes = function(word) 27 | local b0,b1,b2,b3; 28 | b3 = AND(word,0xFF); word = RSHIFT(word,8); 29 | b2 = AND(word,0xFF); word = RSHIFT(word,8); 30 | b1 = AND(word,0xFF); word = RSHIFT(word,8); 31 | b0 = AND(word,0xFF); 32 | return b0,b1,b2,b3; 33 | end 34 | 35 | local bytes2dword = function(b0,b1,b2,b3,b4,b5,b6,b7) 36 | local i = bytes2word(b0,b1,b2,b3); 37 | local j = bytes2word(b4,b5,b6,b7); 38 | return (i*0x100000000)+j; 39 | end 40 | 41 | local dword2bytes = function(i) 42 | local b4,b5,b6,b7 = word2bytes(i); 43 | local b0,b1,b2,b3 = word2bytes(Math.floor(i/0x100000000)); 44 | return b0,b1,b2,b3,b4,b5,b6,b7; 45 | end 46 | 47 | local F = function(x,y,z) return OR(AND(x,y),AND(NOT(x),z)); end 48 | local G = function(x,y,z) return XOR(x,XOR(y,z)); end 49 | local H = function(x,y,z) return OR(AND(x,y),OR(AND(x,z),AND(y,z)));end 50 | local I = function(x,y,z) return XOR(x,XOR(y,z)); end 51 | 52 | local SHA1 = function() 53 | 54 | local queue = Queue(); 55 | 56 | local h0 = 0x67452301; 57 | local h1 = 0xEFCDAB89; 58 | local h2 = 0x98BADCFE; 59 | local h3 = 0x10325476; 60 | local h4 = 0xC3D2E1F0; 61 | 62 | local public = {}; 63 | 64 | local processBlock = function() 65 | local a = h0; 66 | local b = h1; 67 | local c = h2; 68 | local d = h3; 69 | local e = h4; 70 | local temp; 71 | local k; 72 | 73 | local w = {}; 74 | for i=0,15 do 75 | w[i] = bytes2word(queue.pop(),queue.pop(),queue.pop(),queue.pop()); 76 | end 77 | 78 | for i=16,79 do 79 | w[i] = LROT((XOR(XOR(w[i-3],w[i-8]),XOR(w[i-14],w[i-16]))),1); 80 | end 81 | 82 | for i=0,79 do 83 | if (0 <= i) and (i <= 19) then 84 | temp = F(b,c,d); 85 | k = 0x5A827999; 86 | elseif (20 <= i) and (i <= 39) then 87 | temp = G(b,c,d); 88 | k = 0x6ED9EBA1; 89 | elseif (40 <= i) and (i <= 59) then 90 | temp = H(b,c,d); 91 | k = 0x8F1BBCDC; 92 | elseif (60 <= i) and (i <= 79) then 93 | temp = I(b,c,d); 94 | k = 0xCA62C1D6; 95 | end 96 | temp = LROT(a,5) + temp + e + k + w[i]; 97 | e = d; 98 | d = c; 99 | c = LROT(b,30); 100 | b = a; 101 | a = temp; 102 | end 103 | 104 | h0 = AND(h0 + a, 0xFFFFFFFF); 105 | h1 = AND(h1 + b, 0xFFFFFFFF); 106 | h2 = AND(h2 + c, 0xFFFFFFFF); 107 | h3 = AND(h3 + d, 0xFFFFFFFF); 108 | h4 = AND(h4 + e, 0xFFFFFFFF); 109 | end 110 | 111 | public.init = function() 112 | queue.reset(); 113 | h0 = 0x67452301; 114 | h1 = 0xEFCDAB89; 115 | h2 = 0x98BADCFE; 116 | h3 = 0x10325476; 117 | h4 = 0xC3D2E1F0; 118 | return public; 119 | end 120 | 121 | 122 | public.update = function(bytes) 123 | for b in bytes do 124 | queue.push(b); 125 | if queue.size() >= 64 then processBlock(); end 126 | end 127 | 128 | return public; 129 | end 130 | 131 | public.finish = function() 132 | local bits = queue.getHead() * 8; 133 | 134 | queue.push(0x80); 135 | while ((queue.size()+7) % 64) < 63 do 136 | queue.push(0x00); 137 | end 138 | 139 | local b0,b1,b2,b3,b4,b5,b6,b7 = dword2bytes(bits); 140 | 141 | queue.push(b0); 142 | queue.push(b1); 143 | queue.push(b2); 144 | queue.push(b3); 145 | queue.push(b4); 146 | queue.push(b5); 147 | queue.push(b6); 148 | queue.push(b7); 149 | 150 | while queue.size() > 0 do 151 | processBlock(); 152 | end 153 | 154 | return public; 155 | end 156 | 157 | public.asBytes = function() 158 | local b0, b1, b2, b3 = word2bytes(h0); 159 | local b4, b5, b6, b7 = word2bytes(h1); 160 | local b8, b9,b10,b11 = word2bytes(h2); 161 | local b12,b13,b14,b15 = word2bytes(h3); 162 | local b16,b17,b18,b19 = word2bytes(h4); 163 | 164 | return {b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19}; 165 | end 166 | 167 | public.asHex = function() 168 | local b0, b1, b2, b3 = word2bytes(h0); 169 | local b4, b5, b6, b7 = word2bytes(h1); 170 | local b8, b9,b10,b11 = word2bytes(h2); 171 | local b12,b13,b14,b15 = word2bytes(h3); 172 | local b16,b17,b18,b19 = word2bytes(h4); 173 | 174 | return String.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 175 | b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19); 176 | end 177 | 178 | return public; 179 | end 180 | 181 | return SHA1; 182 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/init.lua: -------------------------------------------------------------------------------- 1 | local Lockbox = {}; 2 | 3 | --[[ 4 | package.path = "./?.lua;" 5 | .. "./cipher/?.lua;" 6 | .. "./digest/?.lua;" 7 | .. "./kdf/?.lua;" 8 | .. "./mac/?.lua;" 9 | .. "./padding/?.lua;" 10 | .. "./test/?.lua;" 11 | .. "./util/?.lua;" 12 | .. package.path; 13 | ]]-- 14 | Lockbox.ALLOW_INSECURE = false; 15 | 16 | Lockbox.insecure = function() 17 | assert(Lockbox.ALLOW_INSECURE,"This module is insecure! It should not be used in production. If you really want to use it, set Lockbox.ALLOW_INSECURE to true before importing it"); 18 | end 19 | 20 | return Lockbox; 21 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/kdf/pbkdf2.lua: -------------------------------------------------------------------------------- 1 | local Bit = require("lockbox.util.bit"); 2 | local String = require("string"); 3 | local Array = require("lockbox.util.array"); 4 | local Stream = require("lockbox.util.stream"); 5 | local Math = require("math"); 6 | 7 | local AND = Bit.band; 8 | local OR = Bit.bor; 9 | local NOT = Bit.bnot; 10 | local XOR = Bit.bxor; 11 | local LROT = Bit.lrotate; 12 | local RROT = Bit.rrotate; 13 | local LSHIFT = Bit.lshift; 14 | local RSHIFT = Bit.rshift; 15 | 16 | --PBKDF2 is big-endian 17 | local bytes2word = function(b0,b1,b2,b3) 18 | local i = b0; i = LSHIFT(i,8); 19 | i = OR(i,b1); i = LSHIFT(i,8); 20 | i = OR(i,b2); i = LSHIFT(i,8); 21 | i = OR(i,b3); 22 | return i; 23 | end 24 | 25 | local word2bytes = function(word) 26 | local b0,b1,b2,b3; 27 | b3 = AND(word,0xFF); word = RSHIFT(word,8); 28 | b2 = AND(word,0xFF); word = RSHIFT(word,8); 29 | b1 = AND(word,0xFF); word = RSHIFT(word,8); 30 | b0 = AND(word,0xFF); 31 | return b0,b1,b2,b3; 32 | end 33 | 34 | local bytes2dword = function(b0,b1,b2,b3,b4,b5,b6,b7) 35 | local i = bytes2word(b0,b1,b2,b3); 36 | local j = bytes2word(b4,b5,b6,b7); 37 | return (i*0x100000000)+j; 38 | end 39 | 40 | local dword2bytes = function(i) 41 | local b4,b5,b6,b7 = word2bytes(i); 42 | local b0,b1,b2,b3 = word2bytes(i/0x100000000); 43 | return b0,b1,b2,b3,b4,b5,b6,b7; 44 | end 45 | 46 | 47 | 48 | local PBKDF2 = function() 49 | 50 | local public = {}; 51 | 52 | local blockLen = 16; 53 | local dKeyLen = 256; 54 | local iterations = 4096; 55 | 56 | local salt; 57 | local password; 58 | 59 | 60 | local PRF; 61 | 62 | local dKey; 63 | 64 | 65 | public.setBlockLen = function(len) 66 | blockLen = len; 67 | return public; 68 | end 69 | 70 | public.setDKeyLen = function(len) 71 | dKeyLen = len 72 | return public; 73 | end 74 | 75 | public.setIterations = function(iter) 76 | iterations = iter; 77 | return public; 78 | end 79 | 80 | public.setSalt = function(saltBytes) 81 | salt = saltBytes; 82 | return public; 83 | end 84 | 85 | public.setPassword = function(passwordBytes) 86 | password = passwordBytes; 87 | return public; 88 | end 89 | 90 | public.setPRF = function(prf) 91 | PRF = prf; 92 | return public; 93 | end 94 | 95 | local buildBlock = function(i) 96 | local b0,b1,b2,b3 = word2bytes(i); 97 | local ii = {b0,b1,b2,b3}; 98 | local s = Array.concat(salt,ii); 99 | 100 | local out = {}; 101 | 102 | PRF.setKey(password); 103 | for c = 1,iterations do 104 | PRF.init() 105 | .update(Stream.fromArray(s)); 106 | 107 | s = PRF.finish().asBytes(); 108 | if(c > 1) then 109 | out = Array.XOR(out,s); 110 | else 111 | out = s; 112 | end 113 | end 114 | 115 | return out; 116 | end 117 | 118 | public.finish = function() 119 | local blocks = Math.ceil(dKeyLen / blockLen); 120 | 121 | dKey = {}; 122 | 123 | for b = 1, blocks do 124 | local block = buildBlock(b); 125 | dKey = Array.concat(dKey,block); 126 | end 127 | 128 | if(Array.size(dKey) > dKeyLen) then dKey = Array.truncate(dKey,dKeyLen); end 129 | 130 | return public; 131 | end 132 | 133 | public.asBytes = function() 134 | return dKey; 135 | end 136 | 137 | public.asHex = function() 138 | return Array.toHex(dKey); 139 | end 140 | 141 | return public; 142 | end 143 | 144 | return PBKDF2; 145 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/mac/hmac.lua: -------------------------------------------------------------------------------- 1 | local Bit = require("lockbox.util.bit"); 2 | local String = require("string"); 3 | local Stream = require("lockbox.util.stream"); 4 | local Array = require("lockbox.util.array"); 5 | 6 | local XOR = Bit.bxor; 7 | 8 | local HMAC = function() 9 | 10 | local public = {}; 11 | local blockSize = 64; 12 | local Digest = nil; 13 | local outerPadding = {}; 14 | local innerPadding = {} 15 | local digest; 16 | 17 | public.setBlockSize = function(bytes) 18 | blockSize = bytes; 19 | return public; 20 | end 21 | 22 | public.setDigest = function(digestModule) 23 | Digest = digestModule; 24 | digest = Digest(); 25 | return public; 26 | end 27 | 28 | public.setKey = function(key) 29 | local keyStream; 30 | 31 | if(Array.size(key) > blockSize) then 32 | keyStream = Stream.fromArray(Digest() 33 | .update(Stream.fromArray(key)) 34 | .finish() 35 | .asBytes()); 36 | else 37 | keyStream = Stream.fromArray(key); 38 | end 39 | 40 | outerPadding = {}; 41 | innerPadding = {}; 42 | 43 | for i=1,blockSize do 44 | local byte = keyStream(); 45 | if byte == nil then byte = 0x00; end 46 | outerPadding[i] = XOR(0x5C,byte); 47 | innerPadding[i] = XOR(0x36,byte); 48 | end 49 | 50 | return public; 51 | end 52 | 53 | public.init = function() 54 | digest .init() 55 | .update(Stream.fromArray(innerPadding)); 56 | return public; 57 | end 58 | 59 | public.update = function(messageStream) 60 | digest.update(messageStream); 61 | return public; 62 | end 63 | 64 | public.finish = function() 65 | local inner = digest.finish().asBytes(); 66 | digest .init() 67 | .update(Stream.fromArray(outerPadding)) 68 | .update(Stream.fromArray(inner)) 69 | .finish(); 70 | 71 | return public; 72 | end 73 | 74 | public.asBytes = function() 75 | return digest.asBytes(); 76 | end 77 | 78 | public.asHex = function() 79 | return digest.asHex(); 80 | end 81 | 82 | return public; 83 | 84 | end 85 | 86 | return HMAC; 87 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/padding/ansix923.lua: -------------------------------------------------------------------------------- 1 | local Stream = require("lockbox.util.stream"); 2 | 3 | local ANSIX923Padding = function(blockSize,byteCount) 4 | 5 | local paddingCount = blockSize - (byteCount % blockSize); 6 | local bytesLeft = paddingCount; 7 | 8 | local stream = function() 9 | if bytesLeft > 1 then 10 | bytesLeft = bytesLeft - 1; 11 | return 0x00; 12 | elseif bytesLeft > 0 then 13 | bytesLeft = bytesLeft - 1; 14 | return paddingCount; 15 | else 16 | return nil; 17 | end 18 | end 19 | 20 | return stream; 21 | 22 | end 23 | 24 | return ANSIX923Padding; 25 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/padding/isoiec7816.lua: -------------------------------------------------------------------------------- 1 | local Stream = require("lockbox.util.stream"); 2 | 3 | 4 | local ISOIEC7816Padding = function(blockSize,byteCount) 5 | 6 | local paddingCount = blockSize - (byteCount % blockSize); 7 | local bytesLeft = paddingCount; 8 | 9 | local stream = function() 10 | if bytesLeft == paddingCount then 11 | bytesLeft = bytesLeft - 1; 12 | return 0x80; 13 | elseif bytesLeft > 0 then 14 | bytesLeft = bytesLeft - 1; 15 | return 0x00; 16 | else 17 | return nil; 18 | end 19 | end 20 | 21 | return stream; 22 | 23 | end 24 | 25 | return ISOIEC7816Padding; 26 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/padding/pkcs7.lua: -------------------------------------------------------------------------------- 1 | local Stream = require("lockbox.util.stream"); 2 | 3 | local PKCS7Padding = function(blockSize,byteCount) 4 | 5 | local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; 6 | local bytesLeft = paddingCount; 7 | 8 | local stream = function() 9 | if bytesLeft > 0 then 10 | bytesLeft = bytesLeft - 1; 11 | return paddingCount; 12 | else 13 | return nil; 14 | end 15 | end 16 | 17 | return stream; 18 | end 19 | 20 | return PKCS7Padding; 21 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/padding/zero.lua: -------------------------------------------------------------------------------- 1 | local Stream = require("lockbox.util.stream"); 2 | 3 | local ZeroPadding = function(blockSize,byteCount) 4 | 5 | local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; 6 | local bytesLeft = paddingCount; 7 | 8 | local stream = function() 9 | if bytesLeft > 0 then 10 | bytesLeft = bytesLeft - 1; 11 | return 0x00; 12 | else 13 | return nil; 14 | end 15 | end 16 | 17 | return stream; 18 | 19 | end 20 | 21 | return ZeroPadding; 22 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/util/array.lua: -------------------------------------------------------------------------------- 1 | 2 | local String = require("string"); 3 | local Bit = require("lockbox.util.bit"); 4 | 5 | local XOR = Bit.bxor; 6 | 7 | local Array = {}; 8 | 9 | Array.size = function(array) 10 | return #array; 11 | end 12 | 13 | Array.fromString = function(string) 14 | local bytes = {}; 15 | 16 | local i=1; 17 | local byte = String.byte(string,i); 18 | while byte ~= nil do 19 | bytes[i] = byte; 20 | i = i + 1; 21 | byte = String.byte(string,i); 22 | end 23 | 24 | return bytes; 25 | 26 | end 27 | 28 | Array.toString = function(bytes) 29 | local chars = {}; 30 | local i=1; 31 | 32 | local byte = bytes[i]; 33 | while byte ~= nil do 34 | chars[i] = String.char(byte); 35 | i = i+1; 36 | byte = bytes[i]; 37 | end 38 | 39 | return table.concat(chars,""); 40 | end 41 | 42 | Array.fromStream = function(stream) 43 | local array = {}; 44 | local i=1; 45 | 46 | local byte = stream(); 47 | while byte ~= nil do 48 | array[i] = byte; 49 | i = i+1; 50 | byte = stream(); 51 | end 52 | 53 | return array; 54 | end 55 | 56 | Array.readFromQueue = function(queue,size) 57 | local array = {}; 58 | 59 | for i=1,size do 60 | array[i] = queue.pop(); 61 | end 62 | 63 | return array; 64 | end 65 | 66 | Array.writeToQueue = function(queue,array) 67 | local size = Array.size(array); 68 | 69 | for i=1,size do 70 | queue.push(array[i]); 71 | end 72 | end 73 | 74 | Array.toStream = function(array) 75 | local queue = Queue(); 76 | local i=1; 77 | 78 | local byte = array[i]; 79 | while byte ~= nil do 80 | queue.push(byte); 81 | i=i+1; 82 | byte = array[i]; 83 | end 84 | 85 | return queue.pop; 86 | end 87 | 88 | 89 | local fromHexTable = {}; 90 | for i=0,255 do 91 | fromHexTable[String.format("%02X",i)]=i; 92 | fromHexTable[String.format("%02x",i)]=i; 93 | end 94 | 95 | Array.fromHex = function(hex) 96 | local array = {}; 97 | 98 | for i=1,String.len(hex)/2 do 99 | local h = String.sub(hex,i*2-1,i*2); 100 | array[i] = fromHexTable[h]; 101 | end 102 | 103 | return array; 104 | end 105 | 106 | 107 | local toHexTable = {}; 108 | for i=0,255 do 109 | toHexTable[i]=String.format("%02X",i); 110 | end 111 | 112 | Array.toHex = function(array) 113 | local hex = {}; 114 | local i = 1; 115 | 116 | local byte = array[i]; 117 | while byte ~= nil do 118 | hex[i] = toHexTable[byte]; 119 | i=i+1; 120 | byte = array[i]; 121 | end 122 | 123 | return table.concat(hex,""); 124 | 125 | end 126 | 127 | Array.concat = function(a,b) 128 | local concat = {}; 129 | local out=1; 130 | 131 | local i=1; 132 | local byte = a[i]; 133 | while byte ~= nil do 134 | concat[out] = byte; 135 | i = i + 1; 136 | out = out + 1; 137 | byte = a[i]; 138 | end 139 | 140 | local i=1; 141 | local byte = b[i]; 142 | while byte ~= nil do 143 | concat[out] = byte; 144 | i = i + 1; 145 | out = out + 1; 146 | byte = b[i]; 147 | end 148 | 149 | return concat; 150 | end 151 | 152 | Array.truncate = function(a,newSize) 153 | local x = {}; 154 | 155 | for i=1,newSize do 156 | x[i]=a[i]; 157 | end 158 | 159 | return x; 160 | end 161 | 162 | Array.XOR = function(a,b) 163 | local x = {}; 164 | 165 | for k,v in pairs(a) do 166 | x[k] = XOR(v,b[k]); 167 | end 168 | 169 | return x; 170 | end 171 | 172 | Array.substitute = function(input,sbox) 173 | local out = {}; 174 | 175 | for k,v in pairs(input) do 176 | out[k] = sbox[v]; 177 | end 178 | 179 | return out; 180 | end 181 | 182 | Array.permute = function(input,pbox) 183 | local out = {}; 184 | 185 | for k,v in pairs(pbox) do 186 | out[k] = input[v]; 187 | end 188 | 189 | return out; 190 | end 191 | 192 | Array.copy = function(input) 193 | local out = {}; 194 | 195 | for k,v in pairs(input) do 196 | out[k] = v; 197 | end 198 | return out; 199 | end 200 | 201 | Array.slice = function(input,start,stop) 202 | local out = {}; 203 | 204 | for i=start,stop do 205 | out[i-start+1] = input[i]; 206 | end 207 | return out; 208 | end 209 | 210 | return Array; 211 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/util/base64.lua: -------------------------------------------------------------------------------- 1 | local String = require("string"); 2 | local Bit = require("lockbox.util.bit"); 3 | 4 | local Array = require("lockbox.util.array"); 5 | local Stream = require("lockbox.util.stream"); 6 | 7 | local AND = Bit.band; 8 | local OR = Bit.bor; 9 | local NOT = Bit.bnot; 10 | local XOR = Bit.bxor; 11 | local LROT = Bit.lrotate; 12 | local RROT = Bit.rrotate; 13 | local LSHIFT = Bit.lshift; 14 | local RSHIFT = Bit.rshift; 15 | 16 | 17 | local SYMBOLS = { 18 | [0]="A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P", 19 | "Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f", 20 | "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v", 21 | "w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/"}; 22 | 23 | local LOOKUP = {}; 24 | 25 | for k,v in pairs(SYMBOLS) do 26 | LOOKUP[k]=v; 27 | LOOKUP[v]=k; 28 | end 29 | 30 | 31 | local Base64 = {}; 32 | 33 | Base64.fromStream = function(stream) 34 | local bits = 0x00; 35 | local bitCount = 0; 36 | local base64 = {}; 37 | 38 | local byte = stream(); 39 | while byte ~= nil do 40 | bits = OR(LSHIFT(bits,8),byte); 41 | bitCount = bitCount + 8; 42 | while bitCount >= 6 do 43 | bitCount = bitCount - 6; 44 | local temp = RSHIFT(bits,bitCount); 45 | table.insert(base64,LOOKUP[temp]); 46 | bits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount))); 47 | end 48 | byte = stream(); 49 | end 50 | 51 | if (bitCount == 4) then 52 | bits = LSHIFT(bits,2); 53 | table.insert(base64,LOOKUP[bits]); 54 | table.insert(base64,"="); 55 | elseif (bitCount == 2) then 56 | bits = LSHIFT(bits,4); 57 | table.insert(base64,LOOKUP[bits]); 58 | table.insert(base64,"=="); 59 | end 60 | 61 | return table.concat(base64,""); 62 | end 63 | 64 | Base64.fromArray = function(array) 65 | local bits = 0x00; 66 | local bitCount = 0; 67 | local base64 = {}; 68 | 69 | local ind = 1; 70 | 71 | local byte = array[ind]; ind = ind + 1; 72 | while byte ~= nil do 73 | bits = OR(LSHIFT(bits,8),byte); 74 | bitCount = bitCount + 8; 75 | while bitCount >= 6 do 76 | bitCount = bitCount - 6; 77 | local temp = RSHIFT(bits,bitCount); 78 | table.insert(base64,LOOKUP[temp]); 79 | bits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount))); 80 | end 81 | byte = array[ind]; ind = ind + 1; 82 | end 83 | 84 | if (bitCount == 4) then 85 | bits = LSHIFT(bits,2); 86 | table.insert(base64,LOOKUP[bits]); 87 | table.insert(base64,"="); 88 | elseif (bitCount == 2) then 89 | bits = LSHIFT(bits,4); 90 | table.insert(base64,LOOKUP[bits]); 91 | table.insert(base64,"=="); 92 | end 93 | 94 | return table.concat(base64,""); 95 | end 96 | 97 | Base64.fromString = function(string) 98 | return Base64.fromArray(Array.fromString(string)); 99 | end 100 | 101 | 102 | 103 | Base64.toStream = function(base64) 104 | return Stream.fromArray(Base64.toArray(base64)); 105 | end 106 | 107 | Base64.toArray = function(base64) 108 | local bits = 0x00; 109 | local bitCount = 0; 110 | 111 | local bytes = {}; 112 | 113 | for c in String.gmatch(base64,".") do 114 | if (c == "=") then 115 | bits = RSHIFT(bits,2); bitCount = bitCount - 2; 116 | else 117 | bits = LSHIFT(bits,6); bitCount = bitCount + 6; 118 | bits = OR(bits,LOOKUP[c]); 119 | end 120 | 121 | while(bitCount >= 8) do 122 | bitCount = bitCount - 8; 123 | local temp = RSHIFT(bits,bitCount); 124 | table.insert(bytes,temp); 125 | bits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount))); 126 | end 127 | end 128 | 129 | return bytes; 130 | end 131 | 132 | Base64.toString = function(base64) 133 | local bits = 0x00; 134 | local bitCount = 0; 135 | 136 | local chars = {}; 137 | 138 | for c in String.gmatch(base64,".") do 139 | if (c == "=") then 140 | bits = RSHIFT(bits,2); bitCount = bitCount - 2; 141 | else 142 | bits = LSHIFT(bits,6); bitCount = bitCount + 6; 143 | bits = OR(bits,LOOKUP[c]); 144 | end 145 | 146 | while(bitCount >= 8) do 147 | bitCount = bitCount - 8; 148 | local temp = RSHIFT(bits,bitCount); 149 | table.insert(chars,String.char(temp)); 150 | bits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount))); 151 | end 152 | end 153 | 154 | return table.concat(chars,""); 155 | end 156 | 157 | return Base64; 158 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/util/bit.lua: -------------------------------------------------------------------------------- 1 | local ok, e 2 | if not ok then 3 | ok, e = pcall(require, "bit") -- the LuaJIT one ? 4 | end 5 | if not ok then 6 | ok, e = pcall(require, "bit32") -- Lua 5.2 7 | end 8 | if not ok then 9 | ok, e = pcall(require, "bit.numberlua") -- for Lua 5.1, https://github.com/tst2005/lua-bit-numberlua/ 10 | end 11 | if not ok then 12 | error("no bitwise support found", 2) 13 | end 14 | 15 | -- Workaround to support Lua 5.2 bit32 API with the LuaJIT bit one 16 | if e.rol and not e.lrotate then 17 | e.lrotate = e.rol 18 | end 19 | if e.ror and not e.rrotate then 20 | e.rrotate = e.ror 21 | end 22 | 23 | -- fix of OpenComputers' broken bit32 library 24 | if _VERSION:find("5.3") and _OSVERSION then 25 | e.rrotate = function(x, disp) 26 | if disp == 0 then 27 | return x 28 | elseif disp < 0 then 29 | return e.lrotate(x, -disp) 30 | else 31 | disp = e.band(disp, 31) 32 | x = e.band(x, 0xffffffff) 33 | return e.band(e.bor(e.rshift(x, disp), e.lshift(x, (32 - disp))), 0xffffffff) 34 | end 35 | end 36 | 37 | e.lrotate = function(x, disp) 38 | if disp == 0 then 39 | return x 40 | elseif disp < 0 then 41 | return e.rrotate(x, -disp) 42 | else 43 | disp = e.band(disp, 31) 44 | x = e.band(x, 0xffffffff) 45 | return e.band(e.bor(e.lshift(x, disp), r.shift(x, (32 - disp))), 0xffffffff) 46 | end 47 | end 48 | end 49 | 50 | return e 51 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/util/queue.lua: -------------------------------------------------------------------------------- 1 | local Queue = function() 2 | local queue = {}; 3 | local tail = 0; 4 | local head = 0; 5 | 6 | local public = {}; 7 | 8 | public.push = function(obj) 9 | queue[head] = obj; 10 | head = head + 1; 11 | return; 12 | end 13 | 14 | public.pop = function() 15 | if tail < head 16 | then 17 | local obj = queue[tail]; 18 | queue[tail] = nil; 19 | tail = tail + 1; 20 | return obj; 21 | else 22 | return nil; 23 | end 24 | end 25 | 26 | public.size = function() 27 | return head - tail; 28 | end 29 | 30 | public.getHead = function() 31 | return head; 32 | end 33 | 34 | public.getTail = function() 35 | return tail; 36 | end 37 | 38 | public.reset = function() 39 | queue = {}; 40 | head = 0; 41 | tail = 0; 42 | end 43 | 44 | return public; 45 | end 46 | 47 | return Queue; 48 | -------------------------------------------------------------------------------- /lua-lockbox/lockbox/util/stream.lua: -------------------------------------------------------------------------------- 1 | local Queue = require("lockbox.util.queue"); 2 | local String = require("string"); 3 | 4 | local Stream = {}; 5 | 6 | 7 | Stream.fromString = function(string) 8 | local i=0; 9 | return function() 10 | i=i+1; 11 | if(i <= String.len(string)) then 12 | return String.byte(string,i); 13 | else 14 | return nil; 15 | end 16 | end 17 | end 18 | 19 | 20 | Stream.toString = function(stream) 21 | local array = {}; 22 | local i=1; 23 | 24 | local byte = stream(); 25 | while byte ~= nil do 26 | array[i] = String.char(byte); 27 | i = i+1; 28 | byte = stream(); 29 | end 30 | 31 | return table.concat(array,""); 32 | end 33 | 34 | 35 | Stream.fromArray = function(array) 36 | local queue = Queue(); 37 | local i=1; 38 | 39 | local byte = array[i]; 40 | while byte ~= nil do 41 | queue.push(byte); 42 | i=i+1; 43 | byte = array[i]; 44 | end 45 | 46 | return queue.pop; 47 | end 48 | 49 | 50 | Stream.toArray = function(stream) 51 | local array = {}; 52 | local i=1; 53 | 54 | local byte = stream(); 55 | while byte ~= nil do 56 | array[i] = byte; 57 | i = i+1; 58 | byte = stream(); 59 | end 60 | 61 | return array; 62 | end 63 | 64 | 65 | local fromHexTable = {}; 66 | for i=0,255 do 67 | fromHexTable[String.format("%02X",i)]=i; 68 | fromHexTable[String.format("%02x",i)]=i; 69 | end 70 | 71 | Stream.fromHex = function(hex) 72 | local queue = Queue(); 73 | 74 | for i=1,String.len(hex)/2 do 75 | local h = String.sub(hex,i*2-1,i*2); 76 | queue.push(fromHexTable[h]); 77 | end 78 | 79 | return queue.pop; 80 | end 81 | 82 | 83 | 84 | local toHexTable = {}; 85 | for i=0,255 do 86 | toHexTable[i]=String.format("%02X",i); 87 | end 88 | 89 | Stream.toHex = function(stream) 90 | local hex = {}; 91 | local i = 1; 92 | 93 | local byte = stream(); 94 | while byte ~= nil do 95 | hex[i] = toHexTable[byte]; 96 | i=i+1; 97 | byte = stream(); 98 | end 99 | 100 | return table.concat(hex,""); 101 | end 102 | 103 | return Stream; 104 | -------------------------------------------------------------------------------- /lumber/README.md: -------------------------------------------------------------------------------- 1 | ## A basic lumberjack program for OC robot 2 | 3 | ``` 4 | XX XX XX XX 3 ^ 5 | | 6 | XX XX XX XX 2 | Height 7 | | 8 | XX XX XX XX 1 v 9 | ← <>[] 10 | {} 11 | 2 1 12 | <----Width---> 13 | ``` 14 | 15 | The ASCII-picture above represents the farm. 16 | 17 | * `XX`: sapling 18 | * ` `: empty block 19 | * `<>`: robot 20 | * `[]`: chest 21 | * `{}`: charger 22 | * The `←` shows robot's facing. 23 | 24 | Download the program on the robot and type `edit /usr/bin/lumber.lua` to edit 25 | settings. 26 | 27 | You need to change `W` and `H` to the values that are calculated according to 28 | the picture above. 29 | 30 | The `INTERVAL` settings makes a robot wait a certain time before it'll begin 31 | the next cycle. If the interval is set to `false`, robot will only wait until 32 | fully recharged. 33 | 34 | Robot will also drop the tool in the chest under it when it completes a cycle, 35 | unless the `DROPTOOL` setting is set to `false`. This could be used to recharge 36 | or repair the tool. 37 | 38 | The tool is an axe that can chop the whole tree, not only one wood block. 39 | 40 | **You also need to insert some saplings in the bottom-right slot of robot 41 | inventory, and a log in the previous one.** Otherwise the program won't be 42 | able to distinguish a sapling from a log, and so the robot won't chop the 43 | trees. 44 | 45 | ### License 46 | This program uses the CC0 1.0 license, see the `LICENSE` file for more details about the license. 47 | -------------------------------------------------------------------------------- /lumber/lumber.lua: -------------------------------------------------------------------------------- 1 | -- XX XX XX XX 2 2 | -- Height 3 | -- XX XX XX XX 1 4 | -- 2 1<> 5 | -- Width 6 | 7 | local W, H = 7, 11 8 | local INTERVAL = 30 9 | local DROPTOOL = false 10 | 11 | local com = require("component") 12 | local robot = require("robot") 13 | local term = require("term") 14 | local comp = require("computer") 15 | 16 | local magnet = com.tractor_beam 17 | local inv = com.inventory_controller 18 | local robocom = com.robot 19 | 20 | local INV = robot.inventorySize() 21 | 22 | local wrappers = { 23 | rpt = function(f, to) 24 | to = to or .25 25 | return function(...) 26 | while not f(...) == true do 27 | os.sleep(to) 28 | end 29 | os.sleep(0) 30 | end 31 | end, 32 | detect = function(f, to) 33 | to = to or .25 34 | return function(...) 35 | while true do 36 | local s, rsn = f(...) 37 | if s then return true end 38 | if rsn == "entity" then 39 | robot.swing() 40 | elseif rsn == "air" then 41 | return true 42 | elseif rsn == "nothing selected" then 43 | return false 44 | else 45 | os.sleep(to) 46 | end 47 | end 48 | end 49 | end 50 | } 51 | 52 | local r = { 53 | fwd = wrappers.detect(robot.forward), 54 | back = wrappers.rpt(robot.back), 55 | left = wrappers.rpt(robot.turnLeft), 56 | right = wrappers.rpt(robot.turnRight), 57 | around = wrappers.rpt(robot.turnAround), 58 | up = wrappers.rpt(robot.up), 59 | down = wrappers.rpt(robot.down), 60 | suck = function() 61 | robot.select(INV) 62 | while magnet.suck() do 63 | os.sleep(0.05) 64 | end 65 | if robot.count() ~= 64 then 66 | for i = 1, INV, 1 do 67 | if robot.count(i) > 0 then 68 | if robocom.compareTo(i) then 69 | robot.select(i) 70 | robocom.transferTo(INV) 71 | robot.select(INV) 72 | end 73 | end 74 | end 75 | end 76 | end, 77 | swing = wrappers.detect(function() 78 | if robot.durability() > .1 then 79 | return robot.swing() 80 | end 81 | return true 82 | end), 83 | place = wrappers.detect(robot.place) 84 | } 85 | 86 | local function row() 87 | r.right() 88 | for i = 1, H, 1 do 89 | r.fwd() 90 | r.left() 91 | robot.select(INV - 1) 92 | if robocom.compare(3) and robot.durability() and robot.durability() > .1 then 93 | robot.select(INV) 94 | r.swing() 95 | end 96 | if not robot.detect() and robot.count(INV) > 1 then 97 | robot.select(INV) 98 | r.place() 99 | end 100 | robot.select(INV - 1) 101 | r.around() 102 | if robocom.compare(3) and robot.durability() and robot.durability() > .1 then 103 | robot.select(INV) 104 | r.swing() 105 | end 106 | r.suck() 107 | if not robot.detect() and robot.count(INV) > 1 then 108 | robot.select(INV) 109 | r.place() 110 | end 111 | robot.select(INV - 1) 112 | r.left() 113 | if i ~= H then 114 | r.fwd() 115 | end 116 | end 117 | for i = 1, H, 1 do 118 | r.back() 119 | if i % 2 == 0 then 120 | -- collect the saplings when going back 121 | -- useful when fastleafdecay is installed 122 | r.suck() 123 | end 124 | if i ~= H then 125 | r.back() 126 | end 127 | end 128 | end 129 | 130 | local function dropAll() 131 | for i = 1, INV - 2, 1 do 132 | if robot.count(i) > 0 then 133 | robot.select(i) 134 | robot.drop() 135 | end 136 | end 137 | end 138 | 139 | local function field() 140 | if DROPTOOL then 141 | robot.select(1) 142 | robot.suckDown() 143 | inv.equip() 144 | end 145 | robot.select(INV - 1) 146 | for i = 1, W, 1 do 147 | r.fwd() 148 | row() 149 | if i ~= W then 150 | r.left() 151 | for fw = 1, 3, 1 do 152 | r.fwd() 153 | end 154 | end 155 | end 156 | r.right() 157 | for i = 1, W - 1, 1 do 158 | for fw = 1, 4 do 159 | r.fwd() 160 | end 161 | end 162 | r.fwd() 163 | dropAll() 164 | r.around() 165 | if DROPTOOL then 166 | robot.select(1) 167 | inv.equip() 168 | robot.dropDown() 169 | end 170 | end 171 | 172 | while true do 173 | local x, y = term.getCursor() 174 | term.clearLine() 175 | term.setCursor(x, y) 176 | io.write("Running") 177 | field() 178 | if type(INTERVAL) == "number" then 179 | for i = 0, INTERVAL, 1 do 180 | term.setCursor(x, y) 181 | term.clearLine() 182 | io.write("Sleep: " .. i .. " out of " .. INTERVAL) 183 | os.sleep(1) 184 | end 185 | else 186 | local time = 0 187 | repeat 188 | term.setCursor(x, y) 189 | term.clearLine() 190 | io.write("Sleep: " .. time .. " (" .. comp.energy() .. "/" .. comp.maxEnergy() .. ")") 191 | os.sleep(1) 192 | time = time + 1 193 | until comp.energy() / comp.maxEnergy() > .98 194 | end 195 | os.sleep(0) 196 | term.setCursor(x, y) 197 | end 198 | 199 | -- vim: expandtab tabstop=2 shiftwidth=2 smartindent : 200 | -------------------------------------------------------------------------------- /man/gist: -------------------------------------------------------------------------------- 1 | NAME 2 | gist - download and upload files from/to Gist 3 | 4 | SYNOPSIS 5 | gist [-p] [--P=mode] [--d=desc] [-sRqQlriG] [--f=filename] [file] 6 | 7 | DESCRIPTION 8 | This program makes it easy to download and upload files to/from Gist. 9 | 10 | This program requires an internet card with HTTP enabled in the OpenComputers configuration. 11 | 12 | OPTIONS 13 | When uploading, provide a list of files as arguments: = 14 | 15 | -p 16 | Upload files to gist 17 | 18 | --P=mode or --public=mode 19 | [upload] 's' makes gist secret, 'p' makes it public (latter is the default). 20 | 21 | --d=description or --description=description 22 | [upload] Set the description of a gist. 23 | 24 | -s 25 | If uploading, shorten the resulting URL. Otherwise shorten a Github URL given as the first argument. 26 | 27 | -R 28 | Show raw URL to a file. 29 | 30 | -q 31 | Quiet mode. 32 | 33 | -Q 34 | Superquiet mode: do not show errors. 35 | 36 | -l 37 | List files in gist and quit. 38 | 39 | -r 40 | Override the output file even if it exists. 41 | 42 | -i 43 | Show file information. 44 | 45 | -G 46 | Show gist information. 47 | 48 | --f=filename or --file=filename 49 | Specify a gist file to work with. 50 | 51 | -t 52 | Prompt for a GitHub OAuth personal access token (needs to have gist scope to work). Requires OpenComputers 1.6 and higher. 53 | 54 | --t=token or --token=token 55 | Use a specific token. Not recommended, since the token will be visible as plain text. Requires OpenComputers 1.6 and higher. 56 | 57 | --u=gistid 58 | [upload] Instead of posting a new gist, update an existing one with a specific ID. Provide files to modify as if you were uploading them to Gist. Requires a correct token, see -t. Also requires OpenComputers 1.6 and higher. 59 | 60 | GITHUB OAUTH TOKENS 61 | These tokens can be used to increase API limits to 5000 queries and to work with user account. For example, if token is provided when uploading, the resulting gist will be posted under the user account. 62 | You can get these tokens here: https://github.com/settings/tokens 63 | When you do so, do not forget to set the gist scope, otherwise the token will not have any effect for the program other than increasing rate limits. 64 | 65 | EXAMPLES 66 | gist -s -p --P=s --d="Hello, world!" /examples/test.lua=test.lua 67 | Uploads file /example/test.lua as a secret gist with description "Hello, world", and shows the short URL. 68 | 69 | gist -G https://git.io/example 70 | Gets the gist information. 71 | 72 | gist 1a3b5b7c9d1e3f5a7b9c 73 | Prints gist's file contents. 74 | 75 | gist --f=helloworld.c -r https://git.io/example helloworlds/hello.c 76 | Saves the contents of the file helloworld.c in the gist in helloworlds/hello.c, overrides the file if it exists. 77 | 78 | gist -t --u=12345678901234567890 -p --d="New description" /new/file=file.lua 79 | Changes the description of the gist with the ID 12345678901234567980 to "New description", and updates contents of the file file.lua. Prompts for the GitHub OAuth personal access token. 80 | 81 | gist -s -p --d="Hello, world!" --P=s -t /hello/hello.cpp=hello.cpp 82 | Prompts for the GitHub OAuth personal access token, and then uploads the file hello.cpp to Gist. Returns the short URL to the gist. 83 | -------------------------------------------------------------------------------- /man/nanomachines: -------------------------------------------------------------------------------- 1 | NANOMACHINES USER MANUAL 2 | 3 | Congratulations on purchasing this wonderful and useful piece of software! We want to thank you by giving this manual. 4 | It should answer (and probably does) the one question. And the question is-- 5 | Oh, wait. 6 | 7 | HOW TO USE THIS ~~DAMN~~ INCREDIBLE INVENTION? 8 | First thing you need to do to start is to eat the nanomachines. When this is done, the nanomachines will live inside you. So it's a good idea to keep them under your control and not let them to eat you instead. 9 | 10 | 1. Charge them up. Simply stay near by the charger until the battery icon fills up. 11 | 2. Now you need a device with a network card. The best choice is tablet for obvious reasons. 12 | 3. Make sure you are near enough to the device. That is not a problem if you use tablets. 13 | 4. Before you actually start using them, here are the things you should know: 14 | * Nanomachines have a specific amount of inputs. This amount depends on installed mods. 15 | * Every input may or may not have a behavior - either a potion effect, particles or custom effects. 16 | * This is where everything becomes interesting. I'll use two words: 17 | * "Buff" is a positive behavior. 18 | * "Debuff" is a negative behavior. 19 | * You can get powerful buffs as well as very severe problems. You can be hurt when nanomachines ran out of energy or get withering and instant harm effects. 20 | * This manual is written to help you to minimize the risks. 21 | 5. Now you need a strange liquid called "Grog." You probably used it already to craft nanomachines. Keep it near enough from a respawn point. 22 | 6. Make sure there are no chargers nearby. 23 | 7. Think of saving your stuff if you die. Hoppers, gravestones, etc. 24 | 8. Now you are ready. Run `nn init ` to initialize the program. 25 | 9. Type `nn combo` and wait for a quite long time (4-6 minutes). 26 | 10. IF YOU DIED: 27 | * If the situation is pretty bad, you'll have 19 seconds to do the thing below. If it is VERY BAD, you'll be attacked (2.5 ♥), nanomachines will be charged, and the instant harm effect will instantly kill you. 28 | * Anyway, you should take the grog and drink it. 29 | 11. IF YOU ARE ALIVE: 30 | * Print the test result: `nn get` and `nn getcombo`. 31 | * Use `nn on ` to turn on any input you want. 32 | * If you have more than max safe active input count (2 by default), you will be damaged. 33 | 34 | And that's all. Read the program manual (`man nn`), create a group of inputs for an easier control and have fun. 35 | -------------------------------------------------------------------------------- /man/nn: -------------------------------------------------------------------------------- 1 | NAME 2 | nn - control player's nanomachines 3 | 4 | SYNOPSIS 5 | nn [action] [arguments...] 6 | 7 | DESCRIPTION 8 | This program allows a player to control their nanomachines. 9 | 10 | This program requires the wireless modem card. 11 | 12 | ACTIONS 13 | get 14 | Show basic test result. 15 | 16 | reset 17 | Turn off all inputs. 18 | 19 | test [exclude...] 20 | Run the basic test. Type inputs you don't want to turn on if you want. 21 | 22 | init [port] 23 | Set a response port and initialize the program. Run this first. 24 | 25 | g [other parts...] 26 | Send a message to nanomachines. Show a response. 27 | 28 | s [other parts...] 29 | Send a message to nanomachines. Don't wait for processing and response. 30 | 31 | clear 32 | Clear testing results and reset the program to uninitialized state. 33 | 34 | info 35 | Get info about nanomachines. 36 | 37 | on 38 | Turn on an input. 39 | 40 | off 41 | Turn off an input. 42 | 43 | hp 44 | Get player's health. 45 | 46 | hunger 47 | Get player's hunger and saturation levels. 48 | 49 | energy 50 | Get nanomachines' stored energy. 51 | 52 | usage 53 | Get nanomachines' energy usage. 54 | 55 | age 56 | Get player's age. 57 | 58 | name 59 | Get player's name. 60 | 61 | input 62 | Get max safe and hard max active input limits. 63 | 64 | copy 65 | Copy nanomachines configuration to other nanomachines in inventory. 66 | 67 | efon 68 | Get turned on effects. 69 | 70 | combo [exclude...] 71 | Run combinatoric test (1-1, 8-14, etc). Type input nums to exclude them from testing. If you want to exclude a specific combination, type -. The right part should be greater than the left one. 72 | 73 | getcombo 74 | Show combinatoric test results. 75 | 76 | group 77 | group set [other inputs...] 78 | Add a group or modify group's settings with a specific name. 79 | 80 | group del 81 | Remove a group. 82 | 83 | group save 84 | Save group settings to file. 85 | 86 | group on 87 | Turn on all inputs in the group with a specific name. 88 | 89 | group off 90 | Turn off all inputs in the group with a specific name. 91 | 92 | group list 93 | List groups and their inputs. 94 | 95 | EXAMPLES 96 | nn combo 1-5 12 8-15 97 | Runs combinatoric test and excludes input #12 and combinations 1+5 and 8+15 from testing. 98 | -------------------------------------------------------------------------------- /net-flash/README.md: -------------------------------------------------------------------------------- 1 | # net-flash 2 | *A simple remote EEPROM "flasher".* 3 | 4 | Do you know how to do drone or microcontroller program debugging? Probably 5 | yes: you pick up the drone, swap the EEPROM with another one, insert this 6 | EEPROM into a computer or a server, `flash` the new BIOS, take the flashed 7 | EEPROM back, swap it again to insert it into the drone, place the drone at 8 | required position, start the drone and hope the program you've flashed will 9 | work. 10 | 11 | It's okay when you need to do this once, but when you need to do this quite A 12 | LOT of times this becomes **really** tedious and nasty. 13 | 14 | And this 4096 B code size limit just makes you crazy, doesn't it? 15 | 16 | Well, maybe the program I'm going to present won't fully remove the code size 17 | restriction, but at least it will make the debugging a whole lot easier. 18 | 19 | Before you can use this program, you have to flash the provided BIOS. It's a 20 | really simple program that waits for the program sent over the network (yes, 21 | you have to install the wireless card into the drone or microcontroller), 22 | remembers and runs it. 23 | 24 | **Don't forget to replace the default ADDR variable in the code with the actual 25 | address of the modem you want to use.** Or the drone would just ignore 26 | all commands you send. 27 | 28 | Power on the drone and return to the host machine. Now you can use 29 | the `net-flash` command. Run it without arguments -- you'll get a simple help 30 | text. 31 | 32 | The only required argument is the source. The program can either read from a 33 | file (if you pass the path to the file) or from stdin (it's just a simple `-`). 34 | 35 | There's not much to say on the former option. The latter is more interesting. 36 | If you just run `net-flash -`, you'll be prompted for the command to send to 37 | the remote host. But you can use piping. For example, to keep configuration and 38 | program separate from each other, you can use `cat options.cfg program.lua` to 39 | concatenate those two files, and pass the output to the `net-flash` so the 40 | remote receives the concatenated output. 41 | 42 | ``` 43 | $ cat options.cfg program.lua | net-flash - 44 | ``` 45 | 46 | Pretty cool, isn't it? 47 | 48 | Of course, this program has some options for you to tweak. 49 | 50 | * `--c` is the chunk size. You want to keep it at least slightly below the modem max packet size, or the program would just crash. `4096` will be used if not given. 51 | * `--port` is the, uhhh, port to use. Obviously you have to change the port on the remote host if you want to use this option. 52 | * `-r` or `--response` make the program wait for the drone response (it tells if the program crashed or not, and gives the program returned values). You can specify the timeout if you want (if you don't, the program would wait indefinitely). If you don't give the option, though, the program would instantly exit without waiting for the response. 53 | * `--addr` makes the program not to broadcast messages but to send specifically to the remote modem instead. 54 | 55 | Perhaps you would expect more, but that's all. Have fun programming drones and microcontrollers -- it's easier than you think. 56 | -------------------------------------------------------------------------------- /net-flash/bios.lua: -------------------------------------------------------------------------------- 1 | drone = component.proxy(component.list("drone")()) 2 | modem = component.proxy(component.list("modem")()) 3 | 4 | ADDR = "16f260b2-b42a-4cd7-9cfe-e37e67524c55" 5 | 6 | modem.open(1370) 7 | 8 | while true do 9 | drone.setStatusText("Get\nBIOS...") 10 | modem.send(ADDR, 1370, "net-eeprom", "get bios") 11 | 12 | got = false 13 | code = "" 14 | 15 | while not got do 16 | e = {computer.pullSignal(1)} 17 | if e[1] == "modem_message" and e[3] and e[3]:sub(1, #ADDR) == ADDR and e[6] == "net-eeprom" and e[7] == "eeprom" and e[9] then 18 | got = e[8] 19 | code = code .. e[9] 20 | end 21 | end 22 | 23 | drone.setStatusText("Compile\ncode...") 24 | 25 | chunk, reason = load(code) 26 | if not chunk then 27 | modem.send(ADDR, 1370, "net-eeprom", "error", "compile", reason) 28 | else 29 | drone.setStatusText("Running") 30 | result = {xpcall(chunk, debug.traceback)} 31 | if result[1] then 32 | for k, v in pairs(result) do 33 | result[k] = tostring(v) 34 | end 35 | modem.send(ADDR, 1370, "net-eeprom", "success", table.unpack(result, 2, 7)) 36 | else 37 | modem.send(ADDR, 1370, "net-eeprom", "error", "runtime", tostring(result[2])) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /net-flash/net-flash.lua: -------------------------------------------------------------------------------- 1 | local com = require("component") 2 | local comp = require("computer") 3 | local event = require("event") 4 | local serialization = require("serialization") 5 | local shell = require("shell") 6 | 7 | local modem = com.modem 8 | 9 | local args, options = shell.parse(...) 10 | if #args ~= 1 then 11 | io.stderr:write([==[ 12 | Usage: net-flash [--c=] 13 | [--port=] 14 | [{-r|--response=}] 15 | [--addr=] 16 | ]==]) 17 | return 1 18 | end 19 | 20 | local function send(port, ...) 21 | if type(options.addr) == "string" then 22 | modem.send(options.addr, port, ...) 23 | else 24 | modem.broadcast(port, ...) 25 | end 26 | end 27 | 28 | local input = io.stdin 29 | if args[1] ~= "-" then 30 | local reason 31 | input, reason = io.open(args[1], "r") 32 | if not input then 33 | io.stderr:write("Could not open file for writing: " .. tostring(reason) .. "\n") 34 | return 2 35 | end 36 | end 37 | 38 | local bios = input:read("*a") 39 | input:close() 40 | 41 | local chunks = {} 42 | local chunkSize = tonumber(options.c or options.chunk) 43 | if not chunkSize then 44 | local deviceInfo = comp.getDeviceInfo()[modem.address] 45 | chunkSize = deviceInfo.capacity - 1024 46 | end 47 | 48 | for i = 1, #bios, chunkSize do 49 | table.insert(chunks, bios:sub(i, i + chunkSize - 1)) 50 | end 51 | 52 | local port = tonumber(options.p or options.port) or 1370 53 | for i = 1, #chunks, 1 do 54 | local isEnd = i == #chunks 55 | send(port, "net-eeprom", "eeprom", isEnd, chunks[i]) 56 | end 57 | 58 | if options.r or options.response then 59 | local wasOpen = modem.isOpen(port) 60 | if not wasOpen then 61 | modem.open(port) 62 | end 63 | local timeout = tonumber(options.r) or tonumber(options.response) or math.huge 64 | local addr = type(options.addr) == "string" and options.addr or nil 65 | local e = {event.pull(timeout, "modem_message", _, addr, port, _, "net-eeprom")} 66 | if e[1] then 67 | if e[7] == "success" then 68 | local lastNonNil = 8 69 | for i = 13, 8, -1 do 70 | if e[i] ~= nil then 71 | lastNonNil = i 72 | break 73 | end 74 | end 75 | local response = {} 76 | for i = 8, lastNonNil, 1 do 77 | table.insert(response, serialization.serialize(e[i])) 78 | end 79 | io.stdout:write("Success\t" .. table.concat(response, "\t") .. "\n") 80 | else 81 | io.stdout:write("Error\t" .. e[8] .. "\t" .. (e[9] ~= nil and tostring(e[9]) or "") .. "\n") 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /nn/README.md: -------------------------------------------------------------------------------- 1 | # nn 2 | *Gives you control of your nanomachines.* 3 | 4 | ## Synopsis 5 | `nn [action] [agruments...]` 6 | 7 | ## Description 8 | This program allows the player to control their's nanomachines. 9 | 10 | ## Requirements 11 | * Wireless modem card 12 | 13 | ## Actions 14 | * `get` 15 | * Show result of the basic test. 16 | * `clear` 17 | * Turn off all the inputs. 18 | * `test [exclude...]` 19 | * Run the basic test. Type the inputs you don't want to turn on if you want. 20 | * `init [port]` 21 | * The main action: sets the response port and initializes program. **Run this first.** 22 | * `g [other parts...]` 23 | * Send the message to nanomachines. Show the reply. 24 | * `s [other parts...]` 25 | * Send the message to nanomachines. Don't wait for processing and the reply. 26 | * `reset` 27 | * Clear the testing results. 28 | * `info` 29 | * Get info about nanomachines. 30 | * `on ` 31 | * Turn on the input. 32 | * `off ` 33 | * Turn off the input. 34 | * `hp` 35 | * Get player's health. 36 | * `hunger` 37 | * Get player's hunger and saturation levels. 38 | * `energy` 39 | * Get nanomachines' energy. 40 | * `usage` 41 | * Get nanomachines' energy usage. 42 | * `age` 43 | * Get player's age. 44 | * `name` 45 | * Get player's name. 46 | * `input` 47 | * Get max safe and hard max active inputs limits. 48 | * `copy` 49 | * Save the nanomachines configration to other nanomachines in inventory. 50 | * `efon` 51 | * Get currently active effects. 52 | * `combo [exclude...]` 53 | * Run combinatoric test (1-1, 8-14, etc). Tyoe the input nums to exclude them from testing. If you want to exclude a specific combination, type `-`. Right part should be greater than left one. 54 | * `getcombo` 55 | * Get combinatoric test results 56 | * `group` 57 | * `group set [other inputs...]` 58 | * Add a group or modify group's settings with the name ``. 59 | * `group del ` 60 | * Remove a group. 61 | * `group save` 62 | * Save the group settings to a file. 63 | * `group on ` 64 | * Turn on all the inputs in the group with the name ``. 65 | * `group off ` 66 | * Turn off all the inputs in the group with the name ``. 67 | * `group list` 68 | * List groups and their inputs. 69 | 70 | ## Examples 71 | * `nn combo 1-5 12 8-15` 72 | * Runs the combinatoric test and excludes input #12 and combinations 1+5 and 8+15 from testing. 73 | 74 | ## License 75 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 76 | -------------------------------------------------------------------------------- /opg-chat/chat-modules/admin.module: -------------------------------------------------------------------------------- 1 | local event = require("event") 2 | 3 | addListener("chat_event_part", "admin.event.onUserPart", function(evt, time, chan, user) 4 | local users = {} 5 | for k, _ in pairs(channels[chan].users) do 6 | table.insert(users, k) 7 | end 8 | if not users[1] and chan ~= cfg.main_channel then 9 | channels[chan] = nil -- ba-DOOOM 10 | end 11 | end) 12 | 13 | addListener("chat_event_join", "admin.event.onUserJoin", function(evt, time, chan, user) 14 | if isin(cfg.admins, user) or cfg.server == user then 15 | setMode(chan, cfg.server, "+o", user) 16 | end 17 | end) 18 | 19 | command { 20 | name = "mode", 21 | level = NORMAL, 22 | help = "Sets channel's modes", 23 | func = function(evt, chan, user, raw, cmd, channel, mode, arg) 24 | if not mode then 25 | help(user, "mode") 26 | return -1 27 | end 28 | local success, reason = apcall(setMode, channel, user, mode, arg) 29 | if not success then 30 | sendPM(cfg.server, user, "Could not set mode: " .. (reason or "unknown reason")) 31 | end 32 | end 33 | } 34 | 35 | command { 36 | name = "stop", 37 | level = ADMIN, 38 | help = "Shut down the server", 39 | aliases = {"shutdown", "halt"}, 40 | func = function(evt, chan, user, raw, cmd) 41 | event.push("chat_stop", os.time()) 42 | end 43 | } 44 | 45 | -- vim: expandtab tabstop=2 shiftwidth=2 syntax=lua : 46 | -------------------------------------------------------------------------------- /opg-chat/chat-modules/channels.module: -------------------------------------------------------------------------------- 1 | command { 2 | name = "join", 3 | level = NORMAL, 4 | help = "Join a channel", 5 | aliases = {"j"}, 6 | func = function(evt, chan, user, raw, cmd, channel) 7 | if type(channel) == "string" then 8 | local success, reason = apcall(joinN, channel, user) 9 | if not success then 10 | sendPM(cfg.server, user, "Could not join the channel: " .. (reason or "unknown reason")) 11 | else 12 | local _, pos = isin(users[user].channels, channel) 13 | users[user].currentTab = pos 14 | if #users[user].channels > 9 then 15 | users[user].tabStart = #users[user].channels - 9 16 | end 17 | end 18 | else 19 | help(user, "join") 20 | end 21 | end 22 | } 23 | 24 | command { 25 | name = "part", 26 | level = NORMAL, 27 | help = "Leave a channel", 28 | func = function(evt, chan, user, raw, cmd, channel, msg) 29 | msg = msg or "" 30 | if isin({"string", "nil"}, type(channel)) then 31 | if tonumber(channel) then 32 | local tn = tonumber(channel) 33 | channel = users[user].channels[tn] 34 | if not channel then 35 | sendPM(user, cfg.server, "Wrong tab") 36 | end 37 | else 38 | channel = chan 39 | end 40 | if channel == cfg.main_channel then 41 | sendPM(cfg.server, user, "You can't leave #main!") 42 | return -1 43 | end 44 | local _, was = isin(users[user].channels, channel) 45 | local success, reason = apcall(partN, channel, user, msg) 46 | if not success then 47 | sendPM(cfg.server, user, "Could not leave the channel: " .. (reason or "unknown reason")) 48 | else 49 | if users[user].currentTab == was then 50 | users[user].currentTab = users[user].currentTab - 1 51 | users[user].startTab = users[user].currentTab - 9 52 | if users[user].startTab < 1 then 53 | users[user].startTab = 1 54 | end 55 | end 56 | end 57 | else 58 | help(user, "part") 59 | end 60 | end 61 | } 62 | 63 | command { 64 | name = "tab", 65 | level = NORMAL, 66 | help = "Open tab", 67 | aliases = {"t"}, 68 | func = function(evt, chan, user, raw, cmd, tab) 69 | if tonumber(tab) then 70 | tab = tonumber(tab) 71 | if tab > 0 and tab <= #users[user].channels then 72 | users[user].currentTab = tab 73 | else 74 | sendPM(cfg.server, user, "Wrong tab num") 75 | return 1 76 | end 77 | elseif type(tab) == "string" then 78 | local exists, pos = isin(users[user].channels, tab) 79 | if exists then 80 | users[user].currentTab = pos 81 | else 82 | sendPM(cfg.server, user, "No such channel") 83 | return 1 84 | end 85 | else 86 | help(user, "tab") 87 | end 88 | users[user].startTab = users[user].currentTab - 9 89 | if users[user].startTab < 1 then 90 | users[user].startTab = 1 91 | end 92 | end 93 | } 94 | 95 | command { 96 | name = "topic", 97 | level = NORMAL, 98 | help = "Set channel's topic", 99 | func = function(evt, chan, user, raw, cmd, channel, ...) 100 | local topic = table.concat({...}, " ") 101 | channel = channel or "" 102 | if channel:sub(1, 1) ~= "#" then 103 | topic = channel .. (topic and topic ~= "" and " " .. topic or "") 104 | channel = chan 105 | end 106 | if not channels[channel] or not channels[channel].users[user] then 107 | sendPM(cfg.server, user, "You are not in the channel") 108 | return -1 109 | end 110 | if not isin(channels[channel].modes, "t") or checkLevel(chan, user, {HALFOP, OP}, true) then 111 | channels[channel].topic = topic 112 | sendNotifyChan(channel, "topic", {user, topic}) 113 | else 114 | sendPM(cfg.server, user, "You are not allowed to do this") 115 | end 116 | end 117 | } 118 | 119 | command { 120 | name = "kick", 121 | level = HALFOP, 122 | help = "Kick a user from a channel", 123 | func = function(evt, chan, user, raw, cmd, channel, arg, msg) 124 | if not channel then 125 | help(user, "kick") 126 | return -1 127 | end 128 | if channel:sub(1, 1) ~= "#" then 129 | msg = arg 130 | arg = channel 131 | channel = chan 132 | end 133 | msg = msg or user 134 | if not arg then 135 | help(user, "kick") 136 | return -1 137 | end 138 | if not users[arg] then 139 | sendPM(cfg.server, user, "No such user") 140 | return 1 141 | end 142 | if not channels[channel] or not channels[channel].users[user] then 143 | sendPM(cfg.server, user, "You are not in the channel") 144 | return 1 145 | end 146 | if channel == cfg.main_channel then 147 | sendPM(cfg.server, user, "You can't kick the user from #main") 148 | return 1 149 | end 150 | if isin(cfg.admins, arg) or cfg.server == arg then 151 | sendPM(cfg.server, user, "You are not allowed to kick the admin") 152 | end 153 | local success, reason = apcall(partN, channel, arg, "Kicked by " .. user .. ": " .. msg) 154 | if not success then 155 | sendPM(cfg.server, user, "Could not kick a user: " .. (reason or "unknown reason")) 156 | end 157 | end 158 | } 159 | 160 | -- vim: autoindent expandtab tabstop=2 shiftwidth=2 syntax=lua : 161 | -------------------------------------------------------------------------------- /opg-chat/chat-modules/help.module: -------------------------------------------------------------------------------- 1 | command { 2 | name = "help", 3 | level = NORMAL, 4 | help = "Shows help", 5 | doc = [[SYNTAX: help [command] 6 | If [command] is not specified, show all the command with their descriptions. 7 | Otherwise, show help for the specific command.]], 8 | func = function(evt, chan, user, raw, cmd, command) 9 | if not command then 10 | sendPM(user, cfg.server, "Commands:") 11 | for cmd, cmdInfo in pairs(commands) do 12 | sendPM(user, cfg.server, " * " .. cmd .. ": " .. (cmdInfo.help or "")) 13 | end 14 | else 15 | local success, reason = apcall(help, user, cmd) 16 | if not success then 17 | sendPM(user, cfg.server, ("Could not display help for command %s: %s"):format(command, (reason or "unknown reason"))) 18 | end 19 | end 20 | end 21 | } 22 | 23 | -- vim: expandtab tabstop=2 shiftwidth=2 syntax=lua : 24 | -------------------------------------------------------------------------------- /opg-chat/chat-modules/hide.module: -------------------------------------------------------------------------------- 1 | -- Toggle chat visibility 2 | 3 | command { 4 | name = "toggle", 5 | level = NORMAL, 6 | help = "Toggles chat visibility", 7 | func = function(evt, chan, user, raw, cmd) 8 | users[user].shown = not users[user].shown 9 | for _, obj in pairs(surfaces[user].surface.getAllObjects()) do 10 | local userdata = obj.getUserdata() or {} 11 | if not users[user].shown then 12 | userdata.visible = obj.getVisible() 13 | obj.setUserdata(userdata) 14 | obj.setVisible(false) 15 | else 16 | local visible = userdata.visible 17 | userdata.visible = nil 18 | obj.setUserdata(userdata) 19 | obj.setVisible(visible) 20 | end 21 | end 22 | end 23 | } 24 | 25 | -- vim: expandtab tabstop=2 shiftwidth=2 syntax=lua : 26 | -------------------------------------------------------------------------------- /opg-chat/chat-modules/user.module: -------------------------------------------------------------------------------- 1 | local com = require("component") 2 | 3 | local data = com.isAvailable("data") 4 | local datablock = com.isAvailable("os_datablock") 5 | 6 | command { 7 | name = "msg", 8 | level = NORMAL, 9 | help = "Send private message to user", 10 | aliases = {"m", "pm"}, 11 | func = function(evt, chan, user, raw, cmd, addressee, ...) 12 | local msg = table.concat({...}, " ") 13 | if not addressee or msg == "" then 14 | help(user, "msg") 15 | return -1 16 | end 17 | local success, reason = apcall(sendPM, addressee, user, msg) 18 | if not success then 19 | sendPM(user, cfg.server, "Could not send a PM: " .. (reason or "unknown reason")) 20 | end 21 | end 22 | } 23 | 24 | if not data and not datablock then 25 | io.stderr:write("Neither data card nor OpenSecurity data block are available, /register will not be available\n") 26 | else 27 | local function hash(text) 28 | checkArg(1, text, "string") 29 | local hashed = "" 30 | if data then 31 | hashed = com.data.md5(text) 32 | elseif datablock then 33 | hashed = com.os_datablock.md5(text) 34 | end 35 | return hashed:gsub(".", function(char) return ("%02X"):format(string.byte(char)) end ) 36 | end 37 | 38 | command { 39 | name = "register", 40 | level = NORMAL, 41 | help = "Set password for your nickname", 42 | doc = [[SYNTAX: register [pass] 43 | If no pass given, remove password protection for your nick. Otherwise, set password to [pass]. 44 | Password §cshould not§r contain spaces. 45 | ]], 46 | aliases = {"auth", "pass"}, 47 | func = function(evt, chan, user, raw, cmd, pass) 48 | if not pass then 49 | users[user].cfg.pass = "" 50 | sendPM(user, cfg.server, "You have unset you password!") 51 | elseif type(pass) == "string" then 52 | users[user].cfg.pass = hash(pass) 53 | sendPM(user, cfg.server, "You have taken steps to protect your nickname from being used by network users!") 54 | end 55 | end 56 | } 57 | end 58 | 59 | -- vim: expandtab tabstop=2 shiftwidth=2 syntax=lua : 60 | -------------------------------------------------------------------------------- /opg-chat/chat.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": "%SERVER%", 3 | "admins": ["Fingercomp"], 4 | "main_channel": "#main", 5 | "net": { 6 | "enabled": true, 7 | "modemStrength": 400, 8 | "ports": { 9 | "6667": true, 10 | "6666": ["244d"] 11 | }, 12 | "ping": { 13 | "enabled": true, 14 | "interval": 180, 15 | "timeout": 180 16 | } 17 | }, 18 | "users": {}, 19 | "max_chan_lines": 750 20 | } 21 | -------------------------------------------------------------------------------- /particly/README.md: -------------------------------------------------------------------------------- 1 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/particly). 2 | 3 | ![Example](https://i.imgur.com/OwKFAz0.png) 4 | -------------------------------------------------------------------------------- /particly/examples/2017.lua: -------------------------------------------------------------------------------- 1 | local image = [[ 2 | ####...###....#...##### 3 | ....#.#...#...#.......# 4 | .###..#...#...#......#. 5 | #.....#...#...#.....#.. 6 | #####..###....#....#... 7 | ]] 8 | 9 | while 1 do 10 | local px, py, pz = -5, 4.8, -5 11 | local pname = "flame" 12 | local pvx, pvy, pvz = 0, 0, 0 13 | local step = .2 14 | local doubleHeight = false 15 | draw(image, {px, py, pz}, pname, {pvx, pvy, pvz}, step, doubleHeight) 16 | if event.pull(.05, "interrupted") then 17 | break 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /particly/particly.lua: -------------------------------------------------------------------------------- 1 | local com = require("component") 2 | local event = require("event") 3 | 4 | local p = com.particle 5 | 6 | local function spawn(pname, x, y, z, pvx, pvy, pvz) 7 | if type(pvx) == "number" then 8 | if type(pvy) == "number" and type(pvz) == "number" then 9 | return p.spawn(pname, x, y, z, pvx, pvy, pvz) 10 | else 11 | return p.spawn(pname, x, y, z, pvx) 12 | end 13 | else 14 | return p.spawn(pname, x, y, z) 15 | end 16 | end 17 | 18 | local function draw(image, pcoords, pname, pv, step, doubleHeight) 19 | pcoords = pcoords or {} 20 | local px, py, pz = table.unpack(pcoords) 21 | px = px or 0 22 | py = py or 0 23 | pz = pz or 0 24 | pname = pname or "flame" 25 | local pvx, pvy, pvz 26 | if type(pv) == "number" then 27 | pvx = pv 28 | elseif type(pv) == "table" then 29 | pvx, pvy, pvz = table.unpack(pv) 30 | end 31 | 32 | local x = 0 33 | for line in image:gmatch("[^\n]+") do 34 | x = x + step * 2 35 | local z = 0 36 | for c in line:gmatch(".") do 37 | z = z + step 38 | if c == "#" then 39 | for i = 1, 5, 1 do 40 | spawn(pname, x + px, py, z + pz, pvx, pvy, pvz) 41 | if doubleHeight then 42 | spawn(pname, x + px + step, py, z + pz, pvx, pvy, pvz) 43 | end 44 | end 45 | end 46 | end 47 | end 48 | end 49 | 50 | return draw 51 | -------------------------------------------------------------------------------- /pipedream/README.md: -------------------------------------------------------------------------------- 1 | # pipedream 2 | *Pipes!* 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/pipedream). 5 | 6 | This is a really simple graphical program that kind of mimics a Windows screensaver. But in 2D. 7 | 8 | [Image](https://i.imgur.com/OTvZmv3.png) 9 | -------------------------------------------------------------------------------- /pipedream/pipedream.lua: -------------------------------------------------------------------------------- 1 | local event = require("event") 2 | local term = require("term") 3 | 4 | local gpu = require("component").gpu 5 | 6 | local width, height = gpu.getResolution() 7 | height = height * 2 8 | 9 | local minLen, maxLen = 100, 200 10 | 11 | local pipes = {} 12 | local sides = { 13 | top = 1, 14 | left = 2, 15 | bottom = 3, 16 | right = 4, 17 | "top", 18 | "left", 19 | "bottom", 20 | "right" 21 | } 22 | local char = "▀" 23 | 24 | local turn = 5 25 | 26 | local oldFg, oldBg = gpu.getForeground(), gpu.getBackground() 27 | gpu.fill(1, 1, width, height / 2, " ") 28 | 29 | local spawnInterval = 500 30 | local lifeTime = 600 31 | local clearInterval = 2500 32 | 33 | local i = 0 34 | while true do 35 | if i % spawnInterval == 0 then 36 | local x, y 37 | local side = math.random(1, 4) -- choose one of 4 sides 38 | local direction 39 | if side == sides.top then 40 | x, y = math.random(1, width), 1 41 | direction = sides.bottom 42 | elseif side == sides.left then 43 | x, y = 1, math.random(1, height) 44 | direction = sides.right 45 | elseif side == sides.bottom then 46 | x, y = math.random(1, width), height 47 | direction = sides.top 48 | elseif side == sides.right then 49 | x, y = width, math.random(1, height) 50 | direction = sides.left 51 | end 52 | local length = math.random(minLen, maxLen) 53 | local r, g, b = 0, 0, 0 54 | while r < 127 and g < 127 and b < 127 do 55 | r, g, b = math.random(0, 255), math.random(0, 255), math.random(0, 255) 56 | end 57 | local color = r * 0x10000 + g * 0x100 + b 58 | local pipe = { 59 | x = x, 60 | y = y, 61 | length = length, 62 | dir = direction, 63 | color = color, 64 | life = lifeTime 65 | } 66 | table.insert(pipes, pipe) 67 | end 68 | 69 | for n = #pipes, 1, -1 do 70 | local pipe = pipes[n] 71 | if pipe.life == 0 then 72 | table.remove(pipes, n) 73 | else 74 | local c, fg, bg = gpu.get(pipe.x, 1 + math.floor((pipe.y - 1) / 2)) 75 | if c ~= char then 76 | fg = oldBg 77 | bg = oldBg 78 | end 79 | local part = (pipe.y - 1) % 2 80 | local sBg, sFG 81 | if part == 0 then 82 | sFg = pipe.color 83 | sBg = bg 84 | else 85 | sFg = fg 86 | sBg = pipe.color 87 | end 88 | 89 | if gpu.getForeground() ~= sFg then 90 | gpu.setForeground(sFg) 91 | end 92 | if gpu.getBackground() ~= sBg then 93 | gpu.setBackground(sBg) 94 | end 95 | 96 | gpu.set(pipe.x, 1 + math.floor((pipe.y - 1) / 2), char) 97 | 98 | 99 | local dir = pipe.dir 100 | 101 | local shouldTurn = math.random(1, 100) <= turn 102 | local remains = { 103 | pipe.y - 2, 104 | pipe.x - 2, 105 | height - pipe.y - 1, 106 | width - pipe.x - 1 107 | } 108 | 109 | if remains[dir] == 0 then 110 | shouldTurn = true 111 | end 112 | 113 | if shouldTurn then 114 | local options = {dir - 1, dir + 1} 115 | if options[1] == 0 then 116 | options[1] = 4 117 | end 118 | if options[1] == 5 then 119 | options[1] = 1 120 | end 121 | if options[2] == 0 then 122 | options[2] = 4 123 | end 124 | if options[2] == 5 then 125 | options[2] = 1 126 | end 127 | 128 | if remains[options[2]] < 1 then 129 | table.remove(options, 2) 130 | end 131 | if remains[options[1]] < 1 then 132 | table.remove(options, 1) 133 | end 134 | 135 | dir = options[math.random(1, #options)] 136 | end 137 | 138 | pipe.dir = dir 139 | if pipe.dir == sides.top then 140 | pipe.y = pipe.y - 1 141 | elseif pipe.dir == sides.left then 142 | pipe.x = pipe.x - 1 143 | elseif pipe.dir == sides.right then 144 | pipe.x = pipe.x + 1 145 | elseif pipe.dir == sides.bottom then 146 | pipe.y = pipe.y + 1 147 | end 148 | pipe.life = pipe.life - 1 149 | end 150 | end 151 | i = i + 1 152 | if i >= clearInterval then 153 | if event.pull(2, "interrupted") then 154 | break 155 | end 156 | gpu.setBackground(0x000000) 157 | gpu.setForeground(0xffffff) 158 | gpu.fill(1, 1, width, height / 2, " ") 159 | i = 0 160 | pipes = {} 161 | else 162 | if event.pull(.05, "interrupted") then 163 | break 164 | end 165 | end 166 | end 167 | 168 | gpu.setForeground(oldFg) 169 | gpu.setBackground(oldBg) 170 | 171 | gpu.fill(1, 1, width, height / 2, " ") 172 | 173 | term.setCursor(1, 1) 174 | -------------------------------------------------------------------------------- /railtank/README.md: -------------------------------------------------------------------------------- 1 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/railtank). 2 | 3 | Makes it incredibly easy to monitor your RailCraft tanks. 4 | 5 | * Hooks up tanks attached to computer while the program is running, and handles disconnecting of tanks. 6 | * Looks pretty. 7 | * Has a configuration file. 8 | * Can monitor multiple tanks at the same time. 9 | 10 | ### Usage 11 | Place an adapter right next to a RailCraft tank and install a tank controller upgrade into the adapter. Edit configuration file (`/etc/railtank.cfg`) if needed, and run the program. 12 | 13 | That's it, pretty much. 14 | 15 | ![Screenshot](https://i.imgur.com/G2ATMdU.png) 16 | -------------------------------------------------------------------------------- /smap/README.md: -------------------------------------------------------------------------------- 1 | # smap 2 | **A Simple Minecraft Audio Player** 3 | 4 | **Warning:** no longer maintained. Some things should still work (certainly not 5 | the player, though). 6 | 7 | ## What it does? 8 | It plays audio, does it quite well (oh, okay, not as well as I want). 9 | 10 | ## General info 11 | There are input modules and output modules. 12 | 13 | ### Input modules 14 | They read a file and convert it to internal format. 15 | 16 | Currenlty supported audio files are: 17 | * `nbs`: Minecraft Note Block Studio files. 18 | * `midi`: a clumsy module, mostly a copypaste from Sangar's midi.lua code. Reads MIDI files, and plays it. 19 | 20 | ### Output modules 21 | They convert an internal format audio to actual sounds! 22 | 23 | Currently supported devices are: 24 | * `inoteblock`: Computronics' Iron Note Block. Btw, it's a very good thing. 25 | * `pcspkr`: `computer.beep`'s. 26 | * `beep`: Computronics' beep card. 27 | 28 | ## Big thanks to: 29 | * TxN, for his NBS player code, 30 | * Sangar, for his amazing mod, and midi.lua code as well, 31 | * FluttyProger, for helping and motivating me to fight against bugs. 32 | 33 | ## License 34 | Some pieces of this program use the Apache 2.0 license. Such files have a comment attached at the top with the copyright information. The text of the license can be found [here](http://www.apache.org/licenses/LICENSE-2.0) 35 | -------------------------------------------------------------------------------- /smap/smap/input/nbs.input.lua: -------------------------------------------------------------------------------- 1 | -- Note Block Studio files reader 2 | -- Used: http://pastebin.com/yrtLYBhz 3 | 4 | local fs = require("filesystem") 5 | 6 | NAME = "nbs" 7 | FORMATTYPE = audio.formatTypes.NOTE 8 | 9 | local noteAudio = audio[FORMATTYPE] 10 | 11 | local function note2freq(note) 12 | return 2 ^ ((note - 49) / 12) * 440 13 | end 14 | 15 | local function byte(file) 16 | return string.byte(file:read(1)) 17 | end 18 | 19 | local function nbsInt16(b1, b2) 20 | local n = b1 + 0x100 * b2 21 | n = (n > 0x8000) and (n - 0x7fff) or n 22 | return n 23 | end 24 | 25 | local function nbsInt32(b1, b2, b3, b4) 26 | local n = b1 + b2 * 0x100 + b3 * 0x10000 + b4 * 0x1000000 27 | n = (n > 0x7fffffff) and (n - 0x100000000) or n 28 | return n 29 | end 30 | 31 | local function nbsStr(file) 32 | local strlen = nbsInt32(byte(file), byte(file), byte(file), byte(file)) 33 | return file:read(strlen) 34 | end 35 | 36 | local instr = { 37 | [0] = noteAudio.instr.piano, 38 | [1] = noteAudio.instr.bass, 39 | [2] = noteAudio.instr.drum, 40 | [3] = noteAudio.instr.snare, 41 | [4] = noteAudio.instr.click 42 | } 43 | 44 | function guess(path) 45 | local file, rsn = io.open(path, "rb") 46 | if not file then 47 | return false, rsn 48 | end 49 | local result, rsn = pcall(function() 50 | local length = nbsInt16(byte(file), byte(file)) 51 | 52 | local height = nbsInt16(byte(file), byte(file)) 53 | local name = nbsStr(file) 54 | local author = nbsStr(file) 55 | local originAuthor = nbsStr(file) 56 | local desc = nbsStr(file) 57 | 58 | local tempo = nbsInt16(byte(file), byte(file)) / 100 59 | end) 60 | if result then 61 | return true 62 | end 63 | end 64 | 65 | function loadpath(path) 66 | if fs.isDirectory(path) then 67 | return false, "directories are not supported" 68 | end 69 | 70 | local track 71 | 72 | local function loadBuffer(file, tempo, size) 73 | local buf = noteAudio.Buffer{to=-1, func=function(b) 74 | local newBuf = loadBuffer(file, track.tempo, size) 75 | if newBuf then 76 | track:add(newBuf) 77 | end 78 | end} 79 | 80 | local tick = 0 81 | 82 | while true do 83 | if #buf.data >= size then 84 | break 85 | end 86 | 87 | local jumps = 0 88 | 89 | if pcall(function() 90 | jumps = nbsInt16(byte(file), byte(file)) 91 | end) ~= true then 92 | file:close() 93 | return false, "not a NBS file" 94 | end 95 | 96 | if jumps == 0 then 97 | break 98 | end 99 | 100 | tick = tick + jumps 101 | 102 | local chord = noteAudio.Chord() 103 | if pcall(function() 104 | while true do 105 | local curLayer = nbsInt16(byte(file), byte(file)) 106 | if curLayer == 0 then 107 | break 108 | end 109 | local instrument = byte(file) 110 | local note = byte(file) 111 | local freq = note2freq(note) 112 | chord:add(freq, 1000 / tempo, instr[instrument], 1) 113 | end 114 | end) ~= true then 115 | file:close() 116 | return false, "corrupted file" 117 | end 118 | 119 | buf:add({tick, chord}) 120 | end 121 | 122 | if #buf.data > 0 then 123 | return buf 124 | end 125 | 126 | file:close() 127 | 128 | return nil 129 | end 130 | 131 | local file = io.open(path, "rb") 132 | local length, height, name, author, originAuthor, desc, tempo 133 | if pcall(function() 134 | length = nbsInt16(byte(file), byte(file)) 135 | 136 | height = nbsInt16(byte(file), byte(file)) 137 | name = nbsStr(file) 138 | author = nbsStr(file) 139 | originAuthor = nbsStr(file) 140 | desc = nbsStr(file) 141 | 142 | tempo = nbsInt16(byte(file), byte(file)) / 100 143 | end) ~= true then 144 | file:close() 145 | return false, "not a NBS file" 146 | end 147 | 148 | local trash = file:read(23) 149 | trash = nbsStr(file) 150 | 151 | track = noteAudio.Track{tempo = tempo} 152 | track:setInfo({ 153 | name = name, 154 | author = author, 155 | comment = (originAuthor and originAuthor ~= "" and "Originally created by: " .. originAuthor .. ".\n" or "") .. (desc or "") 156 | }) 157 | local firstBuffer = loadBuffer(file, tempo, math.huge) 158 | 159 | if firstBuffer then 160 | track:add(firstBuffer) 161 | end 162 | 163 | return audio.Music(track, function() 164 | file:close() 165 | end) 166 | end 167 | 168 | -- vim: expandtab tabstop=2 shiftwidth=2 : 169 | -------------------------------------------------------------------------------- /smap/smap/output/beep.output.lua: -------------------------------------------------------------------------------- 1 | -- Beep card module 2 | 3 | local com = require("component") 4 | 5 | NAME = "beep" 6 | DEVICE = "beep" 7 | FORMATTYPE = audio.formatTypes.NOTE 8 | 9 | function new(addr) 10 | if not com.isAvailable("beep") then 11 | return false, "no device connected" 12 | end 13 | addr = addr or com.getPrimary("beep").address 14 | if not com.proxy(addr) then 15 | return false, "no device with such address" 16 | end 17 | local beep = com.proxy(addr) 18 | if not beep.type == "beep" then 19 | return false, "wrong device" 20 | end 21 | return audio.Device(function(self, chords) 22 | local freqPairs = {} 23 | if not com.proxy(addr) then 24 | return false, "device is unavailable" 25 | end 26 | local l = 1 27 | for _, chord in pairs(chords) do 28 | for freq, len, instr in pairs(chord) do 29 | if beep.getBeepCount() + l > 8 then 30 | goto outer 31 | end 32 | while freq < 20 do freq = freq * 2 end 33 | while freq > 2000 do freq = freq / 2 end 34 | freqPairs[freq] = len / 1000 35 | l = l + 1 36 | end 37 | end 38 | ::outer:: 39 | if not com.proxy(addr) then 40 | return false, "device is unavailable" 41 | end 42 | if l > 1 then 43 | beep.beep(freqPairs) 44 | end 45 | end) 46 | end 47 | 48 | -- vim: expandtab tabstop=2 shiftwidth=2 : 49 | -------------------------------------------------------------------------------- /smap/smap/output/inb.output.lua: -------------------------------------------------------------------------------- 1 | -- Computronics Iron Note Blocks output module 2 | 3 | local com = require("component") 4 | 5 | NAME = "inoteblock" 6 | DEVICE = "iron_noteblock" 7 | FORMATTYPE = audio.formatTypes.NOTE 8 | 9 | local function freq2note(freq) 10 | return 12 * math.log(freq / 440, 2) + 49 - 34 11 | end 12 | 13 | local function note2freq(note) 14 | return 2 ^ ((note - 49 + 34) / 12) * 440 15 | end 16 | 17 | local min, max = note2freq(0), note2freq(24) 18 | 19 | function new(addr) 20 | if not com.isAvailable("iron_noteblock") then 21 | return false, "no device connected" 22 | end 23 | addr = addr or com.getPrimary("iron_noteblock").address 24 | if not com.proxy(addr) then 25 | return false, "no device with such address" 26 | end 27 | local noteblock = com.proxy(addr) 28 | if not noteblock.type == "iron_noteblock" then 29 | return false, "wrong device" 30 | end 31 | return audio.Device(function(self, chords) 32 | for _, chord in pairs(chords) do 33 | for freq, len, instr, volume in pairs(chord) do 34 | while freq <= min do freq = freq * 2 end 35 | while freq > max do freq = freq / 2 end 36 | if not com.proxy(addr) then 37 | return false, "device is unavailable" 38 | end 39 | noteblock.playNote(instr - 1, freq2note(freq), volume * self.volume) 40 | end 41 | end 42 | end, FORMATTYPE) 43 | end 44 | 45 | -- vim: expandtab tabstop=2 shiftwidth=2 : 46 | -------------------------------------------------------------------------------- /smap/smap/output/pcspkr.output.lua: -------------------------------------------------------------------------------- 1 | -- computer.beep() 2 | 3 | NAME = "pcspkr" 4 | DEVICE = "computer" 5 | FORMATTYPE = audio.formatTypes.NOTE 6 | 7 | local comp = require("computer") 8 | 9 | function new() 10 | return audio.Device(function(self, chords) 11 | for _, chord in pairs(chords) do 12 | for freq, len, instr in pairs(chord) do 13 | while freq < 20 do freq = freq * 2 end 14 | while freq > 2000 do freq = freq / 2 end 15 | comp.beep(freq, len / 1000) 16 | end 17 | end 18 | end, FORMATTYPE) 19 | end 20 | 21 | -- vim: expandtab tabstop=2 shiftwidth=2 : 22 | -------------------------------------------------------------------------------- /sniff/README.md: -------------------------------------------------------------------------------- 1 | # sniff 2 | 3 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/sniff). 4 | 5 | A utility that shows the network traffic the network card (or linked card) sends and receives. 6 | 7 | ![Screenshot](https://i.imgur.com/hUzd0zg.png) 8 | -------------------------------------------------------------------------------- /stars/README.md: -------------------------------------------------------------------------------- 1 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/stars). 2 | 3 | ![Screenshot](https://i.imgur.com/ULSUwoh.png) 4 | -------------------------------------------------------------------------------- /stars/stars.lua: -------------------------------------------------------------------------------- 1 | local event = require("event") 2 | local unicode = require("unicode") 3 | 4 | local gpu = require("component").gpu 5 | 6 | local chars = { 7 | unicode.char(0x0020), -- [ ] 8 | unicode.char(0x00b7), -- [·] 9 | unicode.char(0x16eb), -- [᛫] 10 | unicode.char(0x22c5), -- [⋅] 11 | unicode.char(0xff65), -- [・] 12 | unicode.char(0x2022), -- [•] 13 | unicode.char(0x25cf) -- [●] 14 | } 15 | 16 | local function line(x0, y0, x1, y1) 17 | local steep = false 18 | if math.abs(x0 - x1) < math.abs(y0 - y1) then 19 | x0, y0 = y0, x0 20 | x1, y1 = y1, x1 21 | steep = true 22 | end 23 | if x0 > x1 then 24 | x0, x1 = x1, x0 25 | y0, y1 = y1, y0 26 | end 27 | local dx = x1 - x0 28 | local dy = y1 - y0 29 | local derr = math.abs(dy) * 2; 30 | local err = 0; 31 | local y = y0 32 | local points = {} 33 | for x = x0, x1, 1 do 34 | if steep then 35 | table.insert(points, {y, x}) 36 | else 37 | table.insert(points, {x, y}) 38 | end 39 | err = err + derr 40 | if err > dx then 41 | if y1 > y0 then 42 | y = y + 1 43 | else 44 | y = y - 1 45 | end 46 | err = err - dx * 2 47 | end 48 | end 49 | return points 50 | end 51 | 52 | local width, height = gpu.getResolution() 53 | local x, y = math.floor(width / 2), math.floor(height / 2) 54 | 55 | local stars = {} 56 | local distance = math.ceil(math.max( 57 | -- top-left 58 | math.sqrt(x^2 + y^2), 59 | -- top-right 60 | math.sqrt((width - x)^2 + y^2), 61 | -- bottom-left 62 | math.sqrt(x^2 + (height - y)^2), 63 | -- bottom-right 64 | math.sqrt((width -x)^2 + (height - y)^2) 65 | )) 66 | 67 | local lines = {} 68 | for line = 1, height, 1 do 69 | lines[line] = {} 70 | for c = 1, width, 1 do 71 | table.insert(lines[line], " ") 72 | end 73 | end 74 | 75 | while true do 76 | for n = 1, 10, 1 do 77 | local angle = math.random(0, 359) 78 | local x1 = math.floor(x + distance * math.cos(math.rad(angle))) 79 | local y1 = math.floor(y + distance * math.sin(math.rad(angle))) 80 | local points = line(x, y, x1, y1) 81 | if points[1][1] ~= x or points[1][2] ~= y then 82 | -- reverse the table 83 | for i = 1, math.floor(#points / 2), 1 do 84 | points[i], points[#points - i + 1] = points[#points - i + 1], points[i] 85 | end 86 | end 87 | for i = 1, #points, 1 do 88 | local p = points[i] 89 | if p[1] < 1 or p[1] > width or p[2] < 1 or p[2] > height then 90 | for j = i, #points, 1 do 91 | points[j] = nil 92 | end 93 | break 94 | end 95 | end 96 | local star = { 97 | points = points, 98 | angle = angle, 99 | i = 0, 100 | state = 2 101 | } 102 | table.insert(stars, star) 103 | end 104 | 105 | for i = #stars, 1, -1 do 106 | local star = stars[i] 107 | star.i = star.i + star.state - 2 + 1 108 | if not star.points[star.i] then 109 | table.remove(stars, i) 110 | else 111 | local sDist = math.sqrt( 112 | (x - star.points[star.i][1])^2 + (y - star.points[star.i][2])^2 113 | ) 114 | star.state = 2 + math.floor(sDist / (distance + 1) * 6) 115 | end 116 | end 117 | 118 | gpu.fill(1, 1, width, height, " ") 119 | for _, line in pairs(lines) do 120 | for k in pairs(line) do 121 | line[k] = " " 122 | end 123 | end 124 | for _, star in pairs(stars) do 125 | local sX, sY = table.unpack(star.points[star.i]) 126 | lines[sY][sX] = chars[star.state] 127 | end 128 | for n, line in pairs(lines) do 129 | gpu.set(1, n, table.concat(line)) 130 | end 131 | 132 | if event.pull(.05, "interrupted") then 133 | break 134 | end 135 | end 136 | 137 | gpu.fill(1, 1, width, height, " ") 138 | -------------------------------------------------------------------------------- /synth/README.md: -------------------------------------------------------------------------------- 1 | # synth 2 | *An easy-to-use interface to the sound card.* 3 | 4 | Available for downloading on the [Hel Repository](https://hel.fomalhaut.me/#packages/synth). 5 | 6 | ### Libraries 7 | This program uses a lot of libraries written by other people. 8 | 9 | * `GUI.lua`, a wonderful GUI library written by @IgorTimofeev. 10 | * `advancedLua.lua` 11 | * `color.lua` -- modified by me to work on Lua 5.3 12 | * `image.lua` -- stripped the depenedncy on OCIF as I don't need it 13 | * `doubleBuffering.lua`, a library; provides a buffer that, when flushed, tries to use as less draw instructions as possible, drawing complex things really fast. Also written by @IgorTimofeev. 14 | * A bundled beautiful plotter library written by @LeshaInc; also modified by me: it now actually works and uses the doubleBuffering library. 15 | 16 | ![Screenshot](https://i.imgur.com/Ahxvlv2.png) 17 | 18 | ## License 19 | This program uses the Apache 2.0 license. The text of the license can be obtained [here](http://www.apache.org/licenses/LICENSE-2.0). 20 | --------------------------------------------------------------------------------