├── .gitignore ├── README.md ├── base-16.lua ├── base-2.lua ├── base-32.lua ├── base-64.lua ├── base-8.lua ├── base-x.lua ├── blake2b.lua ├── blake2s.lua ├── cbor.lua ├── cid-cbor.lua ├── cid.lua ├── connection.lua ├── exchange.lua ├── filecoin.lua ├── gimli-hash.lua ├── gimli-permutation.lua ├── hex.lua ├── ipld.lua ├── isutf8.lua ├── main.lua ├── make-callback.lua ├── mplex.lua ├── msgframe.lua ├── multibase.lua ├── multicodec.lua ├── multihash.lua ├── multiselect.lua ├── notes.md ├── old ├── buffer.lua ├── package.lua └── uv.lua ├── pretty-print.lua ├── protobuf.lua ├── secio.lua ├── sha1.lua ├── sha256.lua ├── sha512.lua ├── stream-wrap.lua ├── switch.lua ├── test-base-64.lua ├── test-base-x.lua ├── test-cid.lua ├── test-filecoin.lua ├── test-gimli.lua ├── test-multibase.lua ├── test-multihash.lua ├── test-sha2.lua ├── tests ├── multibase1.csv ├── multibase2.csv ├── test-exchange.lua ├── test-stream-reader.lua ├── test-uv-ffi.lua ├── test-varint.lua └── utils.lua ├── types.jack ├── u8-array.lua ├── unused-libs ├── blowfish.lua ├── defer.lua ├── prety-print-ffi.lua ├── socket-ffi.lua ├── stream-wrap-ffi.lua ├── uv-ffi.lua ├── uv.h └── wrap-stream.lua ├── uv-socket.lua └── varint.lua /.gitignore: -------------------------------------------------------------------------------- 1 | /deps/ 2 | /lua-filecoin 3 | /.vscode/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Multihash 2 | 3 | There is a basic implementation of multihash here as seen in [multihash.lua](multihash.lua) and [test-multihash.lua](test-multihash.lua). 4 | 5 | Currently this supports the following multihash types: `identity`, `sha`, `sha2-256`, `sha2-512`, `blake2b-*` (8-512), and `blake2s-*` (8-256). 6 | 7 | Usage sample: 8 | 9 | ```lua 10 | local Multihash = require 'multihash' 11 | 12 | -- Multihash.encode(input, hash-name, [length-override]) -> multihash, hash-name 13 | local multihash = Multihash.encode('Hello World', 'blake2b-256') 14 | 15 | -- Multihash.decode(multihash, [index]) -> hash, hash-name, index 16 | local hash, name = Multihash.decode(multihash) 17 | 18 | -- Multihash.verify(input, multihash, [index]) -> verified, index 19 | assert(Multihash.verify('Hello World', multihash), 'Multihash mismatch') 20 | ``` 21 | 22 | The actual implementations of the hash functions are hand-written in lua using luajit's bit and ffi libraries. See [sha256.lua](sha256.lua), [sha512.lua](sha512.lua), [sha1.lua](sha1.lua), [blake2b.lua](blake2b.lua), and [blake2s.lua](blake2s.lua) for details. The main module lazy requires these so only hashes actually used at runtime are ever loaded and compiled. 23 | 24 | ## Multibase 25 | 26 | There is a basic implementation of multibase here as seen in [multibase.lua](multibase.lua) and [test-multibase.lua](test-multibase.lua). 27 | 28 | Currently this supports the following multibase encodings: `identity`, `base2`, `base8`, `base10`, `base16`, `base16upper`, `base32`, `base32upper`, `base32pad`, `base32padupper`, `base32hex`, `base32hexupper`, `base32hexpad`, `base32hexpadupper`, `base32z`, `base58flickr`, `base58btc`, `base64`, `base64pad`, `base64url`, and `base64urlpad`. 29 | 30 | Usage sample: 31 | 32 | ```lua 33 | local Multibase = require 'multibase' 34 | 35 | -- Multibase.encode(raw, name-or-code) -> encoded, name 36 | local encoded = Multibase.encode('Hello World', 'hex') 37 | 38 | -- Multibase.decode(encoded) -> raw, name 39 | local original = Multibase.decode(encoded) 40 | ``` 41 | 42 | The actual implementations of the base functions are hand-written in lua using luajit's bit and ffi libraries. See [base-2.lua](base-2.lua), [base-8.lua](base-8.lua), [base-16.lua](base-16.lua), [base-32.lua](base-32.lua), [base-64.lua](base-64.lua), and [base-x.lua](base-x.lua) for details. The main module lazy requires these so only bases actually used at runtime are ever loaded and compiled. -------------------------------------------------------------------------------- /base-16.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local band = bit.band 3 | local rshift = bit.rshift 4 | local sub = string.sub 5 | local byte = string.byte 6 | local char = string.char 7 | local concat = table.concat 8 | 9 | return function (alphabet) 10 | assert(#alphabet == 16) 11 | local map = {} 12 | for i = 0, 255 do 13 | local chunk = char( 14 | byte(alphabet, band(rshift(i, 4), 15) + 1), 15 | byte(alphabet, band(i, 15) + 1) 16 | ) 17 | map[i] = chunk 18 | map[chunk] = i 19 | end 20 | 21 | local function encode(message) 22 | local parts = {} 23 | for i = 1, #message do 24 | parts[i] = map[byte(message, i)] 25 | end 26 | return concat(parts) 27 | end 28 | 29 | local function decode(encoded) 30 | local missing = #encoded % 2 31 | if missing > 0 then encoded = sub(alphabet, 1, 1) .. encoded end 32 | local parts = {} 33 | local j = 1 34 | for i = 1, #encoded, 2 do 35 | local chunk = sub(encoded, i, i + 1) 36 | parts[j] = char(assert(map[chunk])) 37 | j = j + 1 38 | end 39 | return concat(parts) 40 | end 41 | 42 | return { 43 | encode = encode, 44 | decode = decode 45 | } 46 | end -------------------------------------------------------------------------------- /base-2.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local band = bit.band 3 | local rshift = bit.rshift 4 | local sub = string.sub 5 | local byte = string.byte 6 | local char = string.char 7 | local concat = table.concat 8 | 9 | return function (alphabet) 10 | assert(#alphabet == 2) 11 | local zero = sub(alphabet, 1, 1) 12 | local one = sub(alphabet, 2, 2) 13 | local map = {} 14 | for i = 0, 255 do 15 | local parts = {} 16 | for j = 0, 7 do 17 | parts[8 - j] = band(rshift(i, j), 1) == 0 and zero or one 18 | end 19 | local chunk = concat(parts) 20 | map[i] = chunk 21 | map[chunk] = i 22 | end 23 | 24 | local function encode(message) 25 | local parts = {} 26 | for i = 1, #message do 27 | parts[i] = map[byte(message, i)] 28 | end 29 | return concat(parts) 30 | end 31 | 32 | local function decode(encoded) 33 | local missing = #encoded % 8 34 | if missing > 0 then encoded = zero:rep(8-missing) .. encoded end 35 | local parts = {} 36 | local j = 1 37 | for i = 1, #encoded, 8 do 38 | local chunk = sub(encoded, i, i + 7) 39 | parts[j] = char(assert(map[chunk])) 40 | j = j + 1 41 | end 42 | return concat(parts) 43 | end 44 | 45 | return { 46 | encode = encode, 47 | decode = decode 48 | } 49 | end -------------------------------------------------------------------------------- /base-32.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local rshift = bit.rshift 3 | local lshift = bit.lshift 4 | local bor = bit.bor 5 | local band = bit.band 6 | local char = string.char 7 | local byte = string.byte 8 | local concat = table.concat 9 | 10 | return function (alphabet) 11 | -- Reverse map from character code to 6-bit integer 12 | local map = {} 13 | for i = 1, 32 do 14 | map[byte(alphabet, i)] = i - 1 15 | end 16 | 17 | -- Loop over input 5 bytes at a time 18 | -- a, b, c, d, e are 5 x 8-bit numbers 19 | -- they are encoded into 8 groups of 5-bits 20 | -- aaaaa aaabb bbbbb bcccc ccccd ddddd ddeee eeeee 21 | -- use pad if none of the bits are set. 22 | local function encode(str) 23 | local parts = {} 24 | local j = 1 25 | for i = 1, #str, 5 do 26 | local a, b, c, d, e = byte(str, i, i + 4) 27 | local points = {} 28 | -- aaaaa 29 | points[1] = rshift(a, 3) 30 | -- aaabb 31 | points[2] = bor( 32 | lshift(band(a, 7), 2), 33 | b and rshift(b, 6) or 0 34 | ) 35 | if b then 36 | -- bbbbb 37 | points[3] = band(rshift(b, 1), 31) 38 | -- bcccc 39 | points[4] = bor( 40 | lshift(band(b, 1), 4), 41 | c and rshift(c, 4) or 0 42 | ) 43 | if c then 44 | -- ccccd 45 | points[5] = bor( 46 | lshift(band(c, 15), 1), 47 | d and rshift(d, 7) or 0 48 | ) 49 | if d then 50 | -- ddddd 51 | points[6] = band(rshift(d, 2), 31) 52 | -- ddeee 53 | points[7] = bor( 54 | lshift(band(d, 3), 3), 55 | e and rshift(e, 5) or 0 56 | ) 57 | if e then 58 | -- eeeee 59 | points[8] = band(e, 31) 60 | end 61 | end 62 | end 63 | end 64 | local chars = {} 65 | for k = 1, 8 do 66 | chars[k] = byte(alphabet, (points[k] or 32) + 1) 67 | end 68 | parts[j] = char(unpack(chars)) 69 | j = j + 1 70 | end 71 | return concat(parts) 72 | end 73 | 74 | -- loop over input 8 characters at a time 75 | -- The characters are mapped to 8 x 5-bit integers a,b,c,d,e,f,g,h 76 | -- They need to be reassembled into 5 x 8-bit bytes 77 | -- aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh 78 | local function decode(data) 79 | local parts = {} 80 | local j = 1 81 | for i = 1, #data, 8 do 82 | local a, b, c, d, e, f, g, h = byte(data, i, i + 7) 83 | local chars = {} 84 | b = map[b] 85 | if b then 86 | a = map[a] 87 | -- aaaaabbb 88 | chars[1] = bor( 89 | lshift(a, 3), 90 | rshift(b, 2) 91 | ) 92 | d = map[d] 93 | if d then 94 | c = map[c] 95 | -- bbcccccd 96 | chars[2] = bor( 97 | lshift(band(b, 3), 6), 98 | lshift(c, 1), 99 | rshift(d, 4) 100 | ) 101 | e = map[e] 102 | if e then 103 | -- ddddeeee 104 | chars[3] = bor( 105 | lshift(band(d, 15), 4), 106 | rshift(e, 1) 107 | ) 108 | g = map[g] 109 | if g then 110 | f = map[f] 111 | -- efffffgg 112 | chars[4] = bor( 113 | lshift(band(e, 1), 7), 114 | lshift(f, 2), 115 | rshift(g, 3) 116 | ) 117 | h = map[h] 118 | if h then 119 | -- ggghhhhh 120 | chars[5] = bor( 121 | lshift(band(g, 7), 5), 122 | h 123 | ) 124 | end 125 | end 126 | end 127 | end 128 | end 129 | parts[j] = char(unpack(chars)) 130 | j = j + 1 131 | end 132 | return concat(parts) 133 | end 134 | 135 | return { 136 | encode = encode, 137 | decode = decode, 138 | } 139 | end 140 | -------------------------------------------------------------------------------- /base-64.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local rshift = bit.rshift 3 | local lshift = bit.lshift 4 | local bor = bit.bor 5 | local band = bit.band 6 | local char = string.char 7 | local byte = string.byte 8 | local concat = table.concat 9 | 10 | return function (alphabet) 11 | -- Reverse map from character code to 6-bit integer 12 | local map = {} 13 | for i = 1, 64 do 14 | map[byte(alphabet, i)] = i - 1 15 | end 16 | 17 | -- Loop over input 3 bytes at a time 18 | -- a,b,c are 3 x 8-bit numbers 19 | -- they are encoded into groups of 4 x 6-bit numbers 20 | -- aaaaaa aabbbb bbbbcc cccccc 21 | local function encode(str) 22 | local parts = {} 23 | local j = 1 24 | for i = 1, #str, 3 do 25 | local a, b, c = byte(str, i, i + 2) 26 | local points = {} 27 | -- aaaaaa 28 | points[1] = rshift(a, 2) 29 | -- aabbbb 30 | points[2] = bor( 31 | lshift(band(a, 3), 4), 32 | b and rshift(b, 4) or 0 33 | ) 34 | if b then 35 | -- bbbbcc 36 | points[3] = bor( 37 | lshift(band(b, 15), 2), 38 | c and rshift(c, 6) or 0 39 | ) 40 | if c then 41 | -- cccccc 42 | points[4] = band(c, 63) 43 | end 44 | end 45 | local chars = {} 46 | for k = 1, 4 do 47 | chars[k] = byte(alphabet, (points[k] or 64) + 1) 48 | end 49 | parts[j] = char(unpack(chars)) 50 | j = j + 1 51 | end 52 | return concat(parts) 53 | end 54 | 55 | -- loop over input 4 characters at a time 56 | -- The characters are mapped to 4 x 6-bit integers a,b,c,d 57 | -- They need to be reassembled into 3 x 8-bit bytes 58 | -- aaaaaabb bbbbcccc ccdddddd 59 | local function decode(data) 60 | local parts = {} 61 | local j = 1 62 | for i = 1, #data, 4 do 63 | local a, b, c, d = byte(data, i, i + 3) 64 | local bytes = {} 65 | b = map[b] 66 | if b then 67 | a = map[a] 68 | -- aaaaaabb 69 | bytes[1] = bor( 70 | lshift(a, 2), 71 | rshift(b, 4) 72 | ) 73 | c = map[c] 74 | if c then 75 | -- bbbbcccc 76 | bytes[2] = bor( 77 | lshift(band(b, 15), 4), 78 | rshift(c, 2) 79 | ) 80 | d = map[d] 81 | if d then 82 | -- ccdddddd 83 | bytes[3] = bor( 84 | lshift(band(c, 3), 6), 85 | d 86 | ) 87 | end 88 | end 89 | end 90 | parts[j] = char(unpack(bytes)) 91 | j = j + 1 92 | end 93 | return concat(parts) 94 | end 95 | 96 | return { 97 | encode = encode, 98 | decode = decode, 99 | } 100 | end 101 | -------------------------------------------------------------------------------- /base-8.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local rshift = bit.rshift 3 | local lshift = bit.lshift 4 | local bor = bit.bor 5 | local band = bit.band 6 | local char = string.char 7 | local byte = string.byte 8 | local concat = table.concat 9 | 10 | return function (alphabet) 11 | assert(type(alphabet) == 'string') 12 | -- Reverse map from character code to 6-bit integer 13 | local map = {} 14 | for i = 1, 8 do 15 | map[byte(alphabet, i)] = i - 1 16 | end 17 | 18 | -- Loop over input 3 bytes at a time 19 | -- a, b, c are 3 x 8-bit numbers 20 | -- they are encoded into 8 groups of 3-bits 21 | -- aaa aaa aab bbb bbb bcc ccc ccc 22 | -- use pad if none of the bits are set. 23 | local function encode(str) 24 | local parts = {} 25 | local j = 1 26 | for i = 1, #str, 3 do 27 | local a, b, c = byte(str, i, i + 2) 28 | local points = {} 29 | -- aaa 30 | points[1] = rshift(a, 5) 31 | -- aaa 32 | points[2] = band(rshift(a, 2), 7) 33 | -- aab 34 | points[3] = bor( 35 | lshift(band(a, 3), 1), 36 | b and rshift(b, 7) or 0 37 | ) 38 | if b then 39 | -- bbb 40 | points[4] = band(rshift(b, 4), 7) 41 | -- bbb 42 | points[5] = band(rshift(b, 1), 7) 43 | -- bcc 44 | points[6] = bor( 45 | lshift(band(b, 1), 2), 46 | c and rshift(c, 6) or 0 47 | ) 48 | if c then 49 | -- ccc 50 | points[7] = band(rshift(c, 3), 7) 51 | -- ccc 52 | points[8] = band(c, 7) 53 | end 54 | end 55 | local bytes = {} 56 | for k = 1, 8 do 57 | bytes[k] = byte(alphabet, (points[k] or 8) + 1) 58 | end 59 | parts[j] = char(unpack(bytes)) 60 | j = j + 1 61 | end 62 | return concat(parts) 63 | end 64 | 65 | -- loop over input 8 characters at a time 66 | -- The characters are mapped to 8 x 3-bit integers a,b,c,d,e,f,g,h 67 | -- They need to be reassembled into 3 x 8-bit bytes 68 | -- aaabbbcc cdddeeef fggghhh 69 | local function decode(data) 70 | local parts = {} 71 | local j = 1 72 | for i = 1, #data, 8 do 73 | local a, b, c, d, e, f, g, h = byte(data, i, i + 7) 74 | local bytes = {} 75 | c = map[c] 76 | if c then 77 | a = map[a] 78 | b = map[b] 79 | -- aaabbbcc 80 | bytes[1] = bor( 81 | lshift(a, 5), 82 | lshift(b, 2), 83 | rshift(c, 1) 84 | ) 85 | f = map[f] 86 | if f then 87 | d = map[d] 88 | e = map[e] 89 | -- cdddeeef 90 | bytes[2] = bor( 91 | lshift(band(c, 1), 7), 92 | lshift(d, 4), 93 | lshift(e, 1), 94 | rshift(f, 2) 95 | ) 96 | h = map[h] 97 | if h then 98 | g = map[g] 99 | -- fggghhh 100 | bytes[3] = bor( 101 | lshift(band(f, 1), 6), 102 | lshift(g, 3), 103 | h 104 | ) 105 | end 106 | end 107 | end 108 | parts[j] = char(unpack(bytes)) 109 | j = j + 1 110 | end 111 | return concat(parts) 112 | end 113 | 114 | return { 115 | encode = encode, 116 | decode = decode, 117 | } 118 | end 119 | -------------------------------------------------------------------------------- /base-x.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | local u8Array = ffi.typeof 'uint8_t[?]' 3 | local byte = string.byte 4 | local sub = string.sub 5 | 6 | return function (alphabet) 7 | -- Validate and convert the alphabet 8 | assert(type(alphabet) == 'string', 'Expected string alphabet') 9 | local base = #alphabet 10 | assert(base > 1, 'Alphabet too short') 11 | assert(base < 255, 'Alphabet too long') 12 | 13 | -- Create an inverse map for the base 14 | local baseMap = u8Array(256) 15 | for i = 0, 255 do baseMap[i] = 255 end 16 | for i = 1, base do 17 | local xc = byte(alphabet, i) 18 | if baseMap[xc] ~= 255 then error(string.char(xc) .. ' is ambiguous') end 19 | baseMap[xc] = i - 1 20 | end 21 | 22 | local leader = byte(alphabet, 1) 23 | local factor = math.log(base) / math.log(256) 24 | local ifactor = math.log(256) / math.log(base) 25 | 26 | local function encode (source) 27 | -- Validate input string 28 | assert(type(source) == 'string', "Expected string") 29 | local sourceLength = #source 30 | if sourceLength == 0 then return '' end 31 | 32 | -- Skip & count leading zeroes. 33 | local zeroes = 0 34 | local length = 0 35 | local pbegin = 0 36 | local pend = sourceLength 37 | while pbegin < pend and byte(source, pbegin + 1) == 0 do 38 | pbegin = pbegin + 1 39 | zeroes = zeroes + 1 40 | end 41 | 42 | -- Allocate enough space in big-endian base58 representation. 43 | local size = bit.tobit(((pend - pbegin) * ifactor + 1)) 44 | local b58 = u8Array(size) 45 | 46 | -- Process the bytes. 47 | while pbegin < pend do 48 | local carry = byte(source, pbegin + 1) 49 | 50 | -- Apply "b58 = b58 * 256 + ch". 51 | local i = 0 52 | local it = size - 1 53 | while (carry > 0 or i < length) and it >= 0 do 54 | carry = carry + 256 * b58[it] 55 | b58[it] = carry % base 56 | carry = (carry - b58[it]) / base 57 | it = it - 1 58 | i = i + 1 59 | end 60 | assert(carry == 0, 'Non-zero carry') 61 | length = i 62 | pbegin = pbegin + 1 63 | end 64 | 65 | -- Skip leading zeroes in base58 result. 66 | local it = size - length 67 | while it ~= size and b58[it] == 0 do 68 | it = it + 1 69 | end 70 | 71 | -- Translate the result into a string. 72 | local str = {string.rep(string.char(leader), zeroes) } 73 | while it < size do 74 | local idx = b58[it] + 1 75 | str[#str + 1] = sub(alphabet, idx, idx) 76 | it = it + 1 77 | end 78 | return table.concat(str) 79 | end 80 | 81 | local function decode(source) 82 | -- Validate the source 83 | assert(type(source) == 'string', 'Expected string alphabet') 84 | local sourceLength = #source 85 | if sourceLength == 0 then return "" end 86 | 87 | local psz = 0 88 | 89 | -- Skip and count leading '1's. 90 | local zeroes = 0 91 | local length = 0 92 | while byte(source, psz + 1) == leader do 93 | zeroes = zeroes + 1 94 | psz = psz + 1 95 | end 96 | 97 | -- Allocate enough space in big-endian base256 representation. 98 | local size = bit.tobit(((sourceLength - psz) * factor) + 1) 99 | local b256 = u8Array(size) 100 | 101 | -- Process the characters. 102 | while byte(source, psz + 1) or 0 > 0 do 103 | -- Decode character 104 | local carry = baseMap[byte(source, psz + 1)] 105 | 106 | assert(carry < 255, "Invalid Character") 107 | 108 | local i = 0 109 | local it = size - 1 110 | while (carry ~= 0 or i < length) and it ~= -1 do 111 | carry = carry + (base * b256[it]) 112 | b256[it] = (carry % 256) 113 | carry = bit.rshift(carry, 8) 114 | it = it - 1 115 | i = i + 1 116 | end 117 | 118 | assert(carry == 0, "Non-zero carry") 119 | length = i 120 | psz = psz + 1 121 | end 122 | 123 | -- Skip leading zeroes in b256. 124 | local it = size - length 125 | while it ~= size and b256[it] == 0 do 126 | it = it + 1 127 | end 128 | 129 | local vch = u8Array(zeroes + (size - it)) 130 | 131 | local j = zeroes 132 | -- TODO: optimize with ffi memcopy 133 | while it ~= size do 134 | vch[j] = b256[it] 135 | j = j + 1 136 | it = it + 1 137 | end 138 | 139 | return ffi.string(vch, j) 140 | end 141 | 142 | return { 143 | encode = encode, 144 | decode = decode, 145 | } 146 | end 147 | 148 | -------------------------------------------------------------------------------- /blake2b.lua: -------------------------------------------------------------------------------- 1 | --[[lit-meta 2 | name = "creationix/blake2b" 3 | version = "1.0.1" 4 | homepage = "https://github.com/creationix/luajit-blake2b" 5 | description = "Pure luajit implementation of blake2b." 6 | tags = {"hash", "blake2", "ffi", "luajit"} 7 | license = "MIT" 8 | author = { name = "Tim Caswell" } 9 | ]] 10 | 11 | local bit = require 'bit' 12 | local ffi = require 'ffi' 13 | local ror = bit.ror 14 | local lshift = bit.lshift 15 | local rshift = bit.rshift 16 | local bxor = bit.bxor 17 | local band = bit.band 18 | local bor = bit.bor 19 | local bnot = bit.bnot 20 | local format = string.format 21 | local concat = table.concat 22 | local copy = ffi.copy 23 | local fill = ffi.fill 24 | local sizeof = ffi.sizeof 25 | 26 | ffi.cdef[[ 27 | typedef struct { 28 | uint8_t b[128]; // input buffer 29 | uint64_t h[8]; // chained state 30 | uint64_t t[2]; // total number of bytes 31 | size_t c; // pointer for b[] 32 | size_t outlen; // digest size 33 | } blake2b_ctx; 34 | ]] 35 | 36 | local buffer = ffi.typeof 'uint8_t[?]' 37 | local u64 = ffi.typeof 'uint64_t' 38 | 39 | local IV = ffi.new('uint64_t[8]', { 40 | 0x6A09E667F3BCC908ULL, 0xBB67AE8584CAA73BULL, 41 | 0x3C6EF372FE94F82BULL, 0xA54FF53A5F1D36F1ULL, 42 | 0x510E527FADE682D1ULL, 0x9B05688C2B3E6C1FULL, 43 | 0x1F83D9ABFB41BD6BULL, 0x5BE0CD19137E2179ULL, 44 | }) 45 | 46 | local sigma = ffi.new('uint8_t[12][16]', { 47 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 48 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, 49 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, 50 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, 51 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, 52 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, 53 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, 54 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, 55 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, 56 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, 57 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 58 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, 59 | }) 60 | 61 | -- Little-endian byte access. 62 | local function get64(ptr) 63 | return bor( 64 | u64(ptr[0]), 65 | lshift(u64(ptr[1]), 8), 66 | lshift(u64(ptr[2]), 16), 67 | lshift(u64(ptr[3]), 24), 68 | lshift(u64(ptr[4]), 32), 69 | lshift(u64(ptr[5]), 40), 70 | lshift(u64(ptr[6]), 48), 71 | lshift(u64(ptr[7]), 56) 72 | ) 73 | end 74 | 75 | local new_ctx 76 | 77 | -- local function dump(header, data, w) 78 | -- io.write(header) 79 | -- for i = 0, w - 1 do 80 | -- io.write(format(' %08X%08X', 81 | -- tonumber(rshift(data[i], 32)), 82 | -- tonumber(band(data[i], 0xffffffffULL)) 83 | -- )) 84 | -- if i % 3 == 2 then 85 | -- io.write '\n ' 86 | -- end 87 | -- end 88 | -- io.write '\n\n' 89 | -- end 90 | 91 | local v = ffi.new 'uint64_t[16]' 92 | local m = ffi.new 'uint64_t[16]' 93 | 94 | -- Mixing function G. 95 | local function G(a, b, c, d, x, y) 96 | v[a] = v[a] + v[b] + x 97 | v[d] = ror(bxor(v[d], v[a]), 32) 98 | v[c] = v[c] + v[d] 99 | v[b] = ror(bxor(v[b], v[c]), 24) 100 | v[a] = v[a] + v[b] + y 101 | v[d] = ror(bxor(v[d], v[a]), 16) 102 | v[c] = v[c] + v[d] 103 | v[b] = ror(bxor(v[b], v[c]), 63) 104 | end 105 | 106 | local function ROUND(i) 107 | -- dump(string.format(' (i=%02d) v[16] =', i), v, 16) 108 | G(0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]) 109 | -- dump(string.format(' (i=%02d) v1 =', i), v, 16) 110 | G(1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]) 111 | -- dump(string.format(' (i=%02d) v2 =', i), v, 16) 112 | G(2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]) 113 | -- dump(string.format(' (i=%02d) v3 =', i), v, 16) 114 | G(3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]) 115 | -- dump(string.format(' (i=%02d) v4 =', i), v, 16) 116 | G(0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]) 117 | -- dump(string.format(' (i=%02d) v5 =', i), v, 16) 118 | G(1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]) 119 | -- dump(string.format(' (i=%02d) v6 =', i), v, 16) 120 | G(2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]) 121 | -- dump(string.format(' (i=%02d) v7 =', i), v, 16) 122 | G(3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]) 123 | -- dump(string.format(' (i=%02d) v8 =', i), v, 16) 124 | end 125 | 126 | local Blake2b = {} 127 | 128 | function Blake2b:compress(is_last) 129 | 130 | for i = 0, 7 do -- init work variables 131 | v[i] = self.h[i] 132 | v[i + 8] = IV[i] 133 | end 134 | 135 | v[12] = bxor(v[12], self.t[0]) -- low 32 bits of offset 136 | v[13] = bxor(v[13], self.t[1]) -- high 32 bits 137 | 138 | if is_last then -- last block flag set ? 139 | v[14] = bnot(v[14]) 140 | end 141 | 142 | for i = 0, 15 do -- get little-endian 64-bit words 143 | m[i] = get64(self.b + 8 * i) 144 | end 145 | 146 | -- dump(' m[16] =', m, 16) 147 | 148 | for i = 0, 11 do -- twelve rounds 149 | ROUND(i) 150 | end 151 | 152 | -- dump(' (i=12) v[16] =', v, 16) 153 | 154 | for i = 0, 7 do 155 | self.h[i] = bxor(self.h[i], v[i], v[i + 8]) 156 | end 157 | 158 | -- dump(' h[8] =', self.h, 8) 159 | 160 | end 161 | 162 | function Blake2b.new(outlen, key) 163 | if not outlen then outlen = 64 end 164 | assert(type(outlen) == 'number' and outlen > 0 and outlen <= 64) 165 | if type(key) == 'string' then 166 | local str = key 167 | local len = #str 168 | key = buffer(#key) 169 | copy(key, str, len) 170 | end 171 | local keylen = key and sizeof(key) or 0 172 | 173 | local ctx = new_ctx() 174 | 175 | copy(ctx.h, IV, sizeof(IV)) -- state, "param block" 176 | 177 | ctx.h[0] = bxor(ctx.h[0], 0x01010000, lshift(keylen, 8), outlen) 178 | ctx.t[0] = 0 -- input count low word 179 | ctx.t[1] = 0 -- input count high word 180 | ctx.c = 0 -- pointer within buffer 181 | ctx.outlen = outlen 182 | 183 | if keylen > 0 then 184 | ctx:update(key) 185 | ctx.c = 128 -- at the end 186 | end 187 | 188 | return ctx 189 | end 190 | 191 | function Blake2b:update(input) 192 | assert(input, "Missing input") 193 | if type(input) == 'string' then 194 | local str = input 195 | local len = #str 196 | input = buffer(len) 197 | copy(input, str, len) 198 | end 199 | 200 | for i = 0, sizeof(input) - 1 do 201 | if self.c == 128 then 202 | self.t[0] = self.t[0] + self.c 203 | if self.t[0] < self.c then 204 | self.t[1] = self.t[1] + 1 205 | end 206 | self.c = 0 207 | self:compress(false) 208 | end 209 | self.b[self.c] = input[i] 210 | self.c = self.c + 1 211 | end 212 | end 213 | 214 | function Blake2b:digest(form) 215 | self.t[0] = self.t[0] + self.c 216 | if self.t[0] < self.c then 217 | self.t[1] = self.t[1] + 1 218 | end 219 | 220 | 221 | if self.c < 128 then -- fill up with zeros 222 | fill(self.b + self.c, 128 - self.c) 223 | end 224 | 225 | self:compress(true) 226 | 227 | -- little endian convert and store 228 | local out = buffer(self.outlen) 229 | for i = 0, tonumber(self.outlen) - 1 do 230 | out[i] = rshift(self.h[rshift(i, 3)], 8 * band(i, 7)) 231 | end 232 | 233 | if form == 'string' then 234 | return ffi.string(out, self.outlen) 235 | end 236 | if form == 'hex' then 237 | local hex = {} 238 | for i = 1, tonumber(self.outlen) do 239 | hex[i] = format("%02x", out[i - 1]) 240 | end 241 | return concat(hex) 242 | end 243 | return out 244 | end 245 | 246 | function Blake2b.hash(data, outlen, key, form) 247 | local h = Blake2b.new(outlen, key) 248 | h:update(data) 249 | return h:digest(form) 250 | end 251 | 252 | new_ctx = ffi.metatype('blake2b_ctx', { __index = Blake2b }) 253 | 254 | return Blake2b 255 | -------------------------------------------------------------------------------- /blake2s.lua: -------------------------------------------------------------------------------- 1 | --[[lit-meta 2 | name = "creationix/blake2s" 3 | version = "1.0.2" 4 | homepage = "https://github.com/creationix/luajit-blake2s" 5 | description = "Pure luajit implementation of blake2s." 6 | tags = {"hash", "blake2", "ffi", "luajit"} 7 | license = "MIT" 8 | author = { name = "Tim Caswell" } 9 | ]] 10 | 11 | local bit = require 'bit' 12 | local ffi = require 'ffi' 13 | local ror = bit.ror 14 | local lshift = bit.lshift 15 | local rshift = bit.rshift 16 | local bxor = bit.bxor 17 | local band = bit.band 18 | local bor = bit.bor 19 | local bnot = bit.bnot 20 | local tobit = bit.tobit 21 | local format = string.format 22 | local concat = table.concat 23 | local copy = ffi.copy 24 | local fill = ffi.fill 25 | local sizeof = ffi.sizeof 26 | 27 | ffi.cdef[[ 28 | typedef struct { 29 | uint8_t b[64]; // input buffer 30 | uint32_t h[8]; // chained state 31 | uint32_t t[2]; // total number of bytes 32 | size_t c; // pointer for b[] 33 | size_t outlen; // digest size 34 | } blake2s_ctx; 35 | ]] 36 | 37 | local buffer = ffi.typeof 'uint8_t[?]' 38 | 39 | local IV = ffi.new('uint32_t[8]', { 40 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 41 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 42 | }) 43 | 44 | local sigma = ffi.new('uint8_t[10][16]', { 45 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 46 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, 47 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, 48 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, 49 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, 50 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, 51 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, 52 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, 53 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, 54 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 } 55 | }) 56 | 57 | -- Little-endian byte access. 58 | local function get32(ptr) 59 | return bor( 60 | ptr[0], 61 | lshift(ptr[1], 8), 62 | lshift(ptr[2], 16), 63 | lshift(ptr[3], 24) 64 | ) 65 | end 66 | 67 | local new_ctx 68 | 69 | -- local function dump(header, data, w) 70 | -- io.write(header) 71 | -- for i = 0, w - 1 do 72 | -- io.write(format(' %08X', data[i])) 73 | -- if i % 6 == 5 then 74 | -- io.write '\n ' 75 | -- end 76 | -- end 77 | -- io.write '\n\n' 78 | -- end 79 | 80 | local v = ffi.new 'uint32_t[16]' 81 | local m = ffi.new 'uint32_t[16]' 82 | 83 | -- Mixing function G. 84 | local function G(a, b, c, d, x, y) 85 | v[a] = tobit(v[a] + v[b] + x) 86 | v[d] = ror(bxor(v[d], v[a]), 16) 87 | v[c] = tobit(v[c] + v[d]) 88 | v[b] = ror(bxor(v[b], v[c]), 12) 89 | v[a] = tobit(v[a] + v[b] + y) 90 | v[d] = ror(bxor(v[d], v[a]), 8) 91 | v[c] = tobit(v[c] + v[d]) 92 | v[b] = ror(bxor(v[b], v[c]), 7) 93 | end 94 | 95 | local function ROUND(i) 96 | -- dump(string.format(' (i=%d) v[16] =', i), v, 16) 97 | G(0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]) 98 | -- dump(string.format(' (i=%d) v1 =', i), v, 16) 99 | G(1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]) 100 | -- dump(string.format(' (i=%d) v2 =', i), v, 16) 101 | G(2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]) 102 | -- dump(string.format(' (i=%d) v3 =', i), v, 16) 103 | G(3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]) 104 | -- dump(string.format(' (i=%d) v4 =', i), v, 16) 105 | G(0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]) 106 | -- dump(string.format(' (i=%d) v5 =', i), v, 16) 107 | G(1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]) 108 | -- dump(string.format(' (i=%d) v6 =', i), v, 16) 109 | G(2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]) 110 | -- dump(string.format(' (i=%d) v7 =', i), v, 16) 111 | G(3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]) 112 | -- dump(string.format(' (i=%d) v8 =', i), v, 16) 113 | end 114 | 115 | local Blake2s = {} 116 | 117 | function Blake2s:compress(is_last) 118 | 119 | for i = 0, 7 do -- init work variables 120 | v[i] = self.h[i] 121 | v[i + 8] = IV[i] 122 | end 123 | 124 | v[12] = bxor(v[12], self.t[0]) -- low 32 bits of offset 125 | v[13] = bxor(v[13], self.t[1]) -- high 32 bits 126 | 127 | if is_last then -- last block flag set ? 128 | v[14] = bnot(v[14]) 129 | end 130 | 131 | for i = 0, 15 do -- get little-endian words 132 | m[i] = get32(self.b + 4 * i) 133 | end 134 | 135 | -- dump(' m[16] =', m, 16) 136 | 137 | for i = 0, 9 do -- ten rounds 138 | ROUND(i) 139 | end 140 | 141 | -- dump(' (i=10) v[16] =', v, 16) 142 | 143 | for i = 0, 7 do 144 | self.h[i] = bxor(self.h[i], v[i], v[i + 8]) 145 | end 146 | 147 | -- dump(' h[8] =', self.h, 8) 148 | 149 | end 150 | 151 | function Blake2s.new(outlen, key) 152 | if not outlen then outlen = 32 end 153 | assert(type(outlen) == 'number' and outlen > 0 and outlen <= 32) 154 | if type(key) == 'string' then 155 | local str = key 156 | local len = #str 157 | key = buffer(#key) 158 | copy(key, str, len) 159 | end 160 | local keylen = key and sizeof(key) or 0 161 | 162 | local ctx = new_ctx() 163 | 164 | copy(ctx.h, IV, sizeof(IV)) -- state, "param block" 165 | 166 | ctx.h[0] = bxor(ctx.h[0], 0x01010000, lshift(keylen, 8), outlen) 167 | ctx.t[0] = 0 -- input count low word 168 | ctx.t[1] = 0 -- input count high word 169 | ctx.c = 0 -- pointer within buffer 170 | ctx.outlen = outlen 171 | 172 | if keylen > 0 then 173 | ctx:update(key) 174 | ctx.c = 64 -- at the end 175 | end 176 | 177 | return ctx 178 | end 179 | 180 | function Blake2s:update(input) 181 | if type(input) == 'string' then 182 | local str = input 183 | local len = #str 184 | input = buffer(len) 185 | copy(input, str, len) 186 | end 187 | 188 | for i = 0, sizeof(input) - 1 do 189 | if self.c == 64 then 190 | self.t[0] = self.t[0] + self.c 191 | if self.t[0] < self.c then 192 | self.t[1] = self.t[1] + 1 193 | end 194 | self.c = 0 195 | self:compress(false) 196 | end 197 | self.b[self.c] = input[i] 198 | self.c = self.c + 1 199 | end 200 | end 201 | 202 | function Blake2s:digest(form) 203 | self.t[0] = self.t[0] + self.c 204 | if self.t[0] < self.c then 205 | self.t[1] = self.t[1] + 1 206 | end 207 | 208 | 209 | if self.c < 64 then -- fill up with zeros 210 | fill(self.b + self.c, 64 - self.c) 211 | end 212 | 213 | self:compress(true) 214 | 215 | -- little endian convert and store 216 | local out = buffer(self.outlen) 217 | for i = 0, tonumber(self.outlen) - 1 do 218 | out[i] = rshift(self.h[rshift(i, 2)], 8 * band(i, 3)) 219 | end 220 | 221 | if form == 'string' then 222 | return ffi.string(out, self.outlen) 223 | end 224 | if form == 'hex' then 225 | local hex = {} 226 | for i = 1, tonumber(self.outlen) do 227 | hex[i] = format("%02x", out[i - 1]) 228 | end 229 | return concat(hex) 230 | end 231 | return out 232 | end 233 | 234 | function Blake2s.hash(data, outlen, key, form) 235 | local h = Blake2s.new(outlen, key) 236 | h:update(data) 237 | return h:digest(form) 238 | end 239 | 240 | new_ctx = ffi.metatype('blake2s_ctx', { __index = Blake2s }) 241 | 242 | return Blake2s 243 | -------------------------------------------------------------------------------- /cbor.lua: -------------------------------------------------------------------------------- 1 | --[[lit-meta 2 | name = "creationix/cbor" 3 | version = "1.1.0" 4 | homepage = "https://github.com/creationix/luajit-cbor" 5 | description = "Pure luajit implementation of a subset of cbor." 6 | tags = {"hash", "cbor", "ffi", "luajit"} 7 | license = "MIT" 8 | author = { name = "Tim Caswell" } 9 | ]] 10 | 11 | local ffi = require 'ffi' 12 | local typeof = ffi.typeof 13 | local istype = ffi.istype 14 | local sizeof = ffi.sizeof 15 | local bit = require 'bit' 16 | local bor = bit.bor 17 | local rshift = bit.rshift 18 | local lshift = bit.lshift 19 | local band = bit.band 20 | local char = string.char 21 | local byte = string.byte 22 | local sub = string.sub 23 | local concat = table.concat 24 | 25 | local u64 = typeof 'uint64_t' 26 | local buf = typeof 'uint8_t[?]' 27 | 28 | local function bin(str) 29 | local len = #str 30 | local val = buf(len) 31 | ffi.copy(val, str, len) 32 | return val 33 | end 34 | 35 | local function str(val) 36 | return ffi.string(val, ffi.sizeof(val)) 37 | end 38 | 39 | local tags = {} 40 | local function registerTag(num, meta) 41 | tags[num] = meta 42 | tags[meta] = num 43 | end 44 | 45 | local defaultTag = { 46 | tag = 'CBOR-TAG', 47 | encode = function (obj) 48 | return obj[1] 49 | end, 50 | decode = function (val, tag) 51 | return { val, tag = tag } 52 | end 53 | } 54 | 55 | local function makeTag(tag, val) 56 | return setmetatable(defaultTag.decode(val, tag), defaultTag) 57 | end 58 | 59 | local encoders = {} 60 | local function encode(obj) 61 | local meta = getmetatable(obj) 62 | if meta == defaultTag then return encoders.tag(obj.tag, obj) end 63 | local tag = tags[meta] 64 | if tag then return encoders.tag(tag, obj) end 65 | if meta and meta.encode then obj = meta.encode(obj) end 66 | return encoders[type(obj)](obj) 67 | end 68 | 69 | local function encode_integer(major, num) 70 | assert(num >= 0) 71 | if num >= 0x100000000 and type(num) == 'number' then 72 | num = u64(num) 73 | end 74 | if istype(u64, num) then 75 | return concat { 76 | char(bor(major, 27)), 77 | char(tonumber(rshift(num, 56))), 78 | char(tonumber(band(rshift(num, 48), 0xff))), 79 | char(tonumber(band(rshift(num, 40), 0xff))), 80 | char(tonumber(band(rshift(num, 32), 0xff))), 81 | char(tonumber(band(rshift(num, 24), 0xff))), 82 | char(tonumber(band(rshift(num, 16), 0xff))), 83 | char(tonumber(band(rshift(num, 8), 0xff))), 84 | char(tonumber(band(num, 0xff))) 85 | } 86 | elseif num < 24 then 87 | return char(bor(major, num)) 88 | elseif num < 0x100 then 89 | return char(bor(major, 24)) .. char(num) 90 | elseif num < 0x10000 then 91 | return concat { 92 | char(bor(major, 25)), 93 | char(rshift(num, 8)), 94 | char(band(num, 0xff)) 95 | } 96 | else 97 | return concat { 98 | char(bor(major, 26)), 99 | char(rshift(num, 24)), 100 | char(band(rshift(num, 16), 0xff)), 101 | char(band(rshift(num, 8), 0xff)), 102 | char(band(num, 0xff)) 103 | } 104 | end 105 | end 106 | 107 | encoders.tag = function (tag, value) 108 | local meta = tags[tag] or defaultTag 109 | value = (meta.encode or defaultTag.encode)(value) 110 | return encode_integer(0xc0, tag) .. encode(value) 111 | end 112 | 113 | encoders['nil'] = function () 114 | return '\xf6' 115 | end 116 | 117 | encoders.boolean = function (bool) 118 | return bool and '\xf5' or '\xf4' 119 | end 120 | 121 | encoders.number = function (num) 122 | -- TODO: handle floats 123 | if num >= 0 then 124 | return encode_integer(0x00, num) 125 | else 126 | return encode_integer(0x20, -1 - num) 127 | end 128 | end 129 | 130 | encoders.string = function (str) 131 | return encode_integer(0x60, #str) .. str 132 | end 133 | 134 | encoders.cdata = function (val) 135 | if istype(u64, val) then 136 | return encode_integer(0x00, val) 137 | end 138 | if istype(buf, val) then 139 | local len = sizeof(val) 140 | return encode_integer(0x40, len) .. ffi.string(val, len) 141 | end 142 | error 'Can not encode arbitrary cdata value' 143 | end 144 | 145 | encoders.table = function (tab) 146 | local is_array = true 147 | local key 148 | while true do 149 | local next_key = next(tab, key) 150 | if not next_key then break end 151 | if not key then key = 0 end 152 | is_array = is_array and next_key == key + 1 153 | key = next_key 154 | end 155 | if is_array then 156 | local len = #tab 157 | local parts = { encode_integer(0x80, len) } 158 | for i = 1, len do 159 | parts[i + 1] = encode(tab[i]) 160 | end 161 | return concat(parts) 162 | else 163 | local parts = {} 164 | local count = 0 165 | for k, v in pairs(tab) do 166 | count = count + 1 167 | parts[count * 2 - 1] = encode(k) 168 | parts[count * 2] = encode(v) 169 | end 170 | return encode_integer(0xa0, count) .. concat(parts) 171 | end 172 | end 173 | 174 | local decoders = {} 175 | local function decode(chunk, index) 176 | index = index or 1 177 | local first = byte(chunk, index) 178 | local major = rshift(first, 5) 179 | local minor = band(first, 0x1f) 180 | return decoders[major](minor, chunk, index + 1) 181 | end 182 | 183 | local function decode_u16(chunk, index) 184 | return bor( 185 | lshift(byte(chunk, index), 8), 186 | byte(chunk, index + 1) 187 | ), index + 2 188 | end 189 | 190 | local function decode_u32(chunk, index) 191 | return bor( 192 | lshift(byte(chunk, index), 24), 193 | lshift(byte(chunk, index + 1), 16), 194 | lshift(byte(chunk, index + 2), 8), 195 | byte(chunk, index + 3) 196 | ), index + 4 197 | end 198 | 199 | local function decode_u64(chunk, index) 200 | return bor( 201 | lshift(u64(byte(chunk, index)), 56), 202 | lshift(u64(byte(chunk, index + 1)), 48), 203 | lshift(u64(byte(chunk, index + 2)), 40), 204 | lshift(u64(byte(chunk, index + 3)), 32), 205 | lshift(u64(byte(chunk, index + 4)), 24), 206 | lshift(u64(byte(chunk, index + 5)), 16), 207 | lshift(u64(byte(chunk, index + 6)), 8), 208 | u64(byte(chunk, index + 7)) 209 | ), index + 8 210 | end 211 | 212 | local function major0(minor, chunk, index) 213 | if minor < 24 then 214 | return minor, index 215 | elseif minor == 24 then 216 | return byte(chunk, index), index + 1 217 | elseif minor == 25 then 218 | return decode_u16(chunk, index) 219 | elseif minor == 26 then 220 | return decode_u32(chunk, index) 221 | elseif minor == 27 then 222 | return decode_u64(chunk, index) 223 | else 224 | error 'Unexpected minor value' 225 | end 226 | end 227 | decoders[0] = major0 228 | 229 | decoders[1] = function (minor, chunk, index) 230 | if minor < 24 then 231 | return -minor - 1, index 232 | elseif minor == 24 then 233 | return -byte(chunk, index) - 1, index + 1 234 | elseif minor == 25 then 235 | return -decode_u16(chunk, index) - 1, index + 2 236 | elseif minor == 26 then 237 | return -decode_u32(chunk, index) - 1, index + 4 238 | elseif minor == 27 then 239 | return -decode_u64(chunk, index) - 1, index + 8 240 | else 241 | error 'Unexpected minor value' 242 | end 243 | end 244 | 245 | decoders[2] = function (minor, chunk, index) 246 | local len 247 | len, index = major0(minor, chunk, index) 248 | return buf(len, sub(chunk, index, index + len - 1)), index + len 249 | end 250 | 251 | decoders[3] = function (minor, chunk, index) 252 | local len 253 | len, index = major0(minor, chunk, index) 254 | return sub(chunk, index, index + len - 1), index + len 255 | end 256 | 257 | decoders[4] = function (minor, chunk, index) 258 | local len 259 | len, index = major0(minor, chunk, index) 260 | local parts = {} 261 | for i = 1, len do 262 | local val 263 | val, index = decode(chunk, index) 264 | parts[i] = val 265 | end 266 | return parts, index 267 | end 268 | 269 | decoders[5] = function (minor, chunk, index) 270 | local len 271 | len, index = major0(minor, chunk, index) 272 | local parts = {} 273 | for _ = 1, len do 274 | local key 275 | key, index = decode(chunk, index) 276 | local val 277 | val, index = decode(chunk, index) 278 | parts[key] = val 279 | end 280 | return parts, index 281 | end 282 | 283 | decoders[6] = function (minor, chunk, index) 284 | local value, tag 285 | tag, index = major0(minor, chunk, index) 286 | value, index = decode(chunk, index) 287 | local meta = tags[tag] or defaultTag 288 | value = (meta.decode or defaultTag.decode)(value, tag) 289 | return setmetatable(value, meta), index 290 | end 291 | 292 | decoders[7] = function (minor, _, index) 293 | if minor == 20 then 294 | return false, index 295 | elseif minor == 21 then 296 | return true, index 297 | elseif minor == 22 then 298 | return nil, index 299 | else 300 | error 'Unexpected minor value' 301 | end 302 | end 303 | 304 | return { 305 | u64 = u64, 306 | buf = buf, 307 | bin = bin, 308 | str = str, 309 | registerTag = registerTag, 310 | makeTag = makeTag, 311 | encode = encode, 312 | decode = decode, 313 | } 314 | -------------------------------------------------------------------------------- /cid-cbor.lua: -------------------------------------------------------------------------------- 1 | local Cid = require 'cid' 2 | local Cbor = require 'cbor' 3 | 4 | local CidMeta = Cid.meta 5 | function CidMeta.encode(obj) 6 | return Cbor.bin(Cid.encode(obj)) 7 | end 8 | function CidMeta.decode(bin) 9 | return Cid.decode(Cbor.str(bin)) 10 | end 11 | Cbor.registerTag(42, CidMeta) 12 | -------------------------------------------------------------------------------- /cid.lua: -------------------------------------------------------------------------------- 1 | local Multibase = require 'multibase' 2 | local Multihash = require 'multihash' 3 | local codecs = require 'multicodec' 4 | local Varint = require 'varint' 5 | local sub = string.sub 6 | 7 | local CidMeta = { tag = "CID" } 8 | 9 | -- For v0 CIDs, we assume base58btc encoding. 10 | local function decodeV0(cid) 11 | if #cid ~= 46 or sub(cid, 1, 2) ~= 'Qm' then 12 | return nil, 'Not v0 CID' 13 | end 14 | local hash = Multibase.getBase('z').decode(cid) 15 | local multihash 16 | hash, multihash = Multihash.decode(hash) 17 | return { 18 | version = 0, 19 | multibase = 'base58btc', 20 | multicodec = 'dag-pb', 21 | multihash = multihash, 22 | hash = hash, 23 | } 24 | end 25 | 26 | local function decodeV1(cid, index) 27 | index = index or 1 28 | local version, multibase, multicodec, multihash, hash, bin 29 | 30 | bin, multibase = Multibase.decode(cid) 31 | version, index = Varint.decode(bin, index) 32 | assert(version == 1, 'Expected V1 CID') 33 | multicodec, index = Varint.decode(bin, index) 34 | hash, multihash, index = Multihash.decode(bin, index) 35 | return { 36 | version = version, 37 | multibase = multibase, 38 | multicodec = codecs[multicodec], 39 | multihash = multihash, 40 | hash = hash 41 | }, index 42 | end 43 | 44 | local function encodeV0(obj) 45 | if obj.version then assert(obj.version == 0) end 46 | if obj.multibase then assert(obj.multibase == "base58btc" or obj.multibase == "z") end 47 | if obj.multicodec then assert(obj.multicodec == "dag-pb" or obj.multicodec == 0x70) end 48 | if obj.multihash then assert(obj.multihash == "sha2-256" or obj.multihash == 0x12) end 49 | assert(obj.hash and #obj.hash == 32) 50 | return Multibase.getBase('base58btc').encode(Multihash.encode(obj.hash, "sha2-256")) 51 | end 52 | 53 | local function encodeV1(obj) 54 | if obj.version then assert(obj.version == 1) end 55 | assert(obj.hash and obj.multihash) 56 | local multicodec = obj.multicodec or "raw" 57 | if type(multicodec) == 'string' then multicodec = codecs[multicodec] end 58 | assert(type(multicodec) == 'number', 'Unknown multicodec'); 59 | 60 | return Multibase.encode(table.concat{ 61 | Varint.encode(1), 62 | Varint.encode(multicodec), 63 | Multihash.encode(obj.hash, obj.multihash) 64 | }, obj.multibase or "base58btc") 65 | end 66 | 67 | local function encode(obj) 68 | if obj.version == 0 then return encodeV0(obj) end 69 | if obj.version == 1 then return encodeV1(obj) end 70 | if obj.version == 1 then 71 | local multicodec = obj.multicodec 72 | if type(multicodec) == 'string' then multicodec = codecs[multicodec] end 73 | assert(type(multicodec) == 'number', 'Unknown multicodec'); 74 | return Multibase.encode(table.concat{ 75 | Varint.encode(1), 76 | Varint.encode(multicodec), 77 | Multihash.encode(obj.hash, obj.multihash) 78 | }, obj.multibase) 79 | end 80 | error("Unknown CID version " .. obj.version) 81 | end 82 | 83 | local function decode(cid) 84 | return (decodeV0(cid) or decodeV1(cid)) 85 | end 86 | 87 | local function link(data, options) 88 | options = options or {} 89 | local multihash = options.multihash or "blake2b-256" 90 | return setmetatable({ 91 | version = 1, 92 | multicodec = options.multicodec or 'raw', 93 | multibase = options.multibase or 'base58btc', 94 | multihash = multihash, 95 | hash = Multihash.getHash(multihash)(data) 96 | }, CidMeta) 97 | end 98 | 99 | local function link0(data) 100 | local multihash = 'sha2-256' 101 | return setmetatable({ 102 | version = 0, 103 | multibase = 'base58btc', 104 | multicodec = 'dag-pb', 105 | multihash = multihash, 106 | hash = Multihash.getHash(multihash)(data) 107 | }, CidMeta) 108 | end 109 | 110 | 111 | return { 112 | meta = CidMeta, 113 | decode = decode, 114 | decodeV0 = decodeV0, 115 | decodeV1 = decodeV1, 116 | encode = encode, 117 | encodeV0 = encodeV0, 118 | encodeV1 = encodeV1, 119 | link = link, 120 | link0 = link0, 121 | } -------------------------------------------------------------------------------- /connection.lua: -------------------------------------------------------------------------------- 1 | local byte = string.byte 2 | local char = string.char 3 | 4 | local Connection = {} 5 | 6 | local function wrapRead(readNext) 7 | -- Extra data from the last frame. 8 | local chunk = nil 9 | local index = 0 10 | 11 | -- Get next byte as a number 12 | -- Returns nil on EOS 13 | local function readByte() 14 | while true do 15 | if not chunk then 16 | chunk = readNext() 17 | index = 1 18 | end 19 | if not chunk then 20 | return 21 | end 22 | if index <= #chunk then 23 | local b = byte(chunk, index) 24 | index = index + 1 25 | return b 26 | else 27 | chunk = nil 28 | end 29 | end 30 | end 31 | 32 | local function readChunk(length) 33 | local buffer = {} 34 | for i = 1, length do 35 | local b = readByte() 36 | if not b then 37 | break 38 | end 39 | buffer[i] = char(b) 40 | end 41 | return table.concat(buffer) 42 | end 43 | 44 | return readByte, readChunk 45 | end 46 | 47 | Connection.wrapRead = wrapRead 48 | 49 | function Connection.newPush() 50 | local queue = {} 51 | local reads = 1 52 | local writes = 1 53 | 54 | local stream = {} 55 | 56 | function stream.onChunk(chunk) 57 | -- If there was a waiting reader, give it the value. 58 | if reads > writes then 59 | local thread 60 | thread, queue[writes] = queue[writes], nil 61 | writes = writes + 1 62 | return assert(coroutine.resume(thread, chunk)) 63 | end 64 | 65 | -- If nobody is waiting for the data, pause it. 66 | if writes > reads and stream.onStop then 67 | stream.onStop() 68 | end 69 | 70 | -- Store the value in the queue waiting for a reader. 71 | queue[writes] = chunk 72 | writes = writes + 1 73 | end 74 | 75 | local function readNext() 76 | -- If there is a value waiting for us, return it. 77 | if writes > reads then 78 | local value 79 | value, queue[reads] = queue[reads], nil 80 | reads = reads + 1 81 | return value 82 | end 83 | 84 | -- If we need more data and the stream is paused, unpause it. 85 | if stream.onStart then 86 | stream.onStart() 87 | end 88 | 89 | -- Wait for the result. 90 | queue[reads] = coroutine.running() 91 | reads = reads + 1 92 | return coroutine.yield() 93 | end 94 | 95 | local readByte, readChunk = wrapRead(readNext) 96 | 97 | stream.readByte = readByte 98 | stream.readChunk = readChunk 99 | 100 | return stream 101 | end 102 | 103 | return Connection 104 | -------------------------------------------------------------------------------- /exchange.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Related reading: 4 | 5 | - https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography 6 | - https://wiki.openssl.org/index.php/Elliptic_Curve_Diffie_Hellman 7 | - https://github.com/openssl/openssl/blob/master/include/openssl/ec.h 8 | 9 | ]] 10 | 11 | local pkey = require 'openssl/pkey' 12 | local bignum = require 'openssl/bignum' 13 | 14 | local NistToASN1 = { 15 | ['P-256'] = 'prime256v1', -- X9.62/SECG curve over a 256 bit prime field 16 | ['P-384'] = 'secp384r1', -- NIST/SECG curve over a 384 bit prime field 17 | ['P-521'] = 'secp521r1' -- NIST/SECG curve over a 521 bit prime field 18 | } 19 | 20 | local Exchange = {} 21 | 22 | local keymeta = getmetatable(pkey.new{ type = "EC", curve = "prime256v1" }) 23 | 24 | function Exchange.generate(curveName) 25 | local asnName = assert(NistToASN1[curveName], 'Unsupported curvename') 26 | return pkey.new{ type = "EC", curve = asnName } 27 | end 28 | 29 | function Exchange.export(key) 30 | assert(getmetatable(key) == keymeta, "Expected pkey") 31 | local point = key:getParameters().pub_key 32 | p("export", point) 33 | return point:toBinary() 34 | end 35 | 36 | function Exchange.import(curveName, str) 37 | local point = bignum.fromBinary(str) 38 | p("import", point) 39 | local asnName = assert(NistToASN1[curveName], 'Unsupported curvename') 40 | local key = pkey.new({type="EC", curve=asnName}) 41 | local params = key:getParameters() 42 | p(params) 43 | key:setParameters{ 44 | priv_key = 0, 45 | pub_key = point, 46 | group = params.group 47 | } 48 | key:setPrivateKey('') 49 | p(key) 50 | p(true) 51 | p(key:getParameters()) 52 | return key 53 | end 54 | 55 | function Exchange.exchange(key, peerkey) 56 | assert(getmetatable(key) == keymeta, "Expected pkey") 57 | assert(getmetatable(peerkey) == keymeta, "Expected pkey") 58 | 59 | local group = C.EC_KEY_get0_group(key) 60 | local fieldSize = C.EC_GROUP_get_degree(group) 61 | local secretLen = math.floor((fieldSize + 7) / 8) 62 | local secret = newBuffer(secretLen) 63 | local point = C.EC_KEY_get0_public_key(peerkey) 64 | local written = C.ECDH_compute_key(secret, secretLen, point, key, nil) 65 | assert(written == secretLen) 66 | return ffi.string(secret, secretLen) 67 | end 68 | 69 | function Exchange.cleanup() 70 | assert(bn) 71 | C.BN_CTX_free(bn) 72 | bn = nil 73 | end 74 | 75 | return Exchange 76 | -------------------------------------------------------------------------------- /filecoin.lua: -------------------------------------------------------------------------------- 1 | local Varint = require 'varint' 2 | local Cbor = require 'cbor' 3 | local bin = Cbor.bin 4 | local str = Cbor.str 5 | require 'cid-cbor' 6 | 7 | -------------------------------------------------------------------------------- 8 | -- Address 9 | -------------------------------------------------------------------------------- 10 | 11 | local Address = {} 12 | local AddressMeta = {__index = Address} 13 | 14 | function Address.new(val) 15 | return setmetatable(val and AddressMeta.decode(val) or {}, AddressMeta) 16 | end 17 | 18 | AddressMeta.tag = "Address" 19 | 20 | function AddressMeta:__tostring() 21 | return self.network .. self.protocol .. tonumber(self.payload) 22 | end 23 | 24 | function AddressMeta:encode() 25 | local protocol = self.protocol 26 | local payload = self.payload 27 | if protocol == 0 then 28 | assert(type(payload) == 'number' and math.floor(payload) == payload, 'Invalid ID payload in address') 29 | return bin(protocol .. Varint.encode(payload)) 30 | else 31 | error 'TODO: support more address protocols' 32 | end 33 | error 'Invalid protocol value in address' 34 | end 35 | 36 | function AddressMeta.decode(val) 37 | local protocol = val[0] - 48 38 | if protocol == 0 then 39 | return { 40 | protocol = protocol, 41 | payload = Varint.decodebin(val, 1) 42 | } 43 | else 44 | error "TODO: decode more protocol versions" 45 | end 46 | end 47 | 48 | -------------------------------------------------------------------------------- 49 | -- Block 50 | -------------------------------------------------------------------------------- 51 | 52 | local Block = {} 53 | local BlockMeta = {__index = Block} 54 | 55 | function Block.new(...) 56 | return setmetatable(BlockMeta.decode{...}, BlockMeta) 57 | end 58 | 59 | BlockMeta.tag = "Block" 60 | 61 | function BlockMeta.encode(obj) 62 | return { 63 | obj.Miner, 64 | obj.Tickets, 65 | obj.ElectionProof, 66 | obj.Parents, 67 | obj.ParentWeight, 68 | obj.Height, 69 | obj.StateRoot, 70 | obj.Messages, 71 | obj.BLSAggregate, 72 | obj.MessageReceipts, 73 | obj.Timestamp, 74 | obj.BlockSig, 75 | } 76 | end 77 | 78 | function BlockMeta.decode(val) 79 | return { 80 | Miner = val[1], 81 | Tickets = val[2], 82 | ElectionProof = val[3], 83 | Parents = val[4], 84 | ParentWeight = val[5], 85 | Height = val[6], 86 | StateRoot = val[7], 87 | Messages = val[8], 88 | BLSAggregate = val[9], 89 | MessageReceipts = val[10], 90 | Timestamp = val[11], 91 | BlockSig = val[12], 92 | } 93 | end 94 | 95 | Cbor.registerTag(43, BlockMeta) 96 | 97 | -------------------------------------------------------------------------------- 98 | -- Message 99 | -------------------------------------------------------------------------------- 100 | 101 | local Message = {} 102 | local MessageMeta = {__index = Message} 103 | 104 | function Message.new(...) 105 | return setmetatable(MessageMeta.decode{...}, MessageMeta) 106 | end 107 | 108 | MessageMeta.tag = "Message" 109 | 110 | function MessageMeta.encode(obj) 111 | return { 112 | obj.To, 113 | obj.From, 114 | obj.Nonce, 115 | obj.Value, 116 | obj.GasPrice, 117 | obj.GasLimit, 118 | obj.Method, 119 | obj.Params, 120 | } 121 | end 122 | 123 | function MessageMeta.decode(val) 124 | return { 125 | To = val[1], 126 | From = val[2], 127 | Nonce = val[3], 128 | Value = val[4], 129 | GasPrice = val[5], 130 | GasLimit = val[6], 131 | Method = val[7], 132 | Params = val[8], 133 | } 134 | end 135 | 136 | Cbor.registerTag(44, MessageMeta) 137 | 138 | -------------------------------------------------------------------------------- 139 | -- SignedMessage 140 | -------------------------------------------------------------------------------- 141 | 142 | local SignedMessage = {} 143 | local SignedMessageMeta = {__index = SignedMessage} 144 | 145 | function SignedMessage.new(...) 146 | return setmetatable(SignedMessageMeta.decode{...}, SignedMessageMeta) 147 | end 148 | 149 | SignedMessageMeta.tag = "SignedMessage" 150 | 151 | function SignedMessageMeta.encode(obj) 152 | return { 153 | obj.Message, 154 | obj.Signature, 155 | } 156 | end 157 | 158 | function SignedMessageMeta.decode(val) 159 | return { 160 | Message = val[1], 161 | Signature = val[2], 162 | } 163 | end 164 | 165 | Cbor.registerTag(45, SignedMessageMeta) 166 | 167 | -------------------------------------------------------------------------------- 168 | -- Exports 169 | -------------------------------------------------------------------------------- 170 | 171 | return { 172 | Address = Address, 173 | Block = Block, 174 | Message = Message, 175 | SignedMessage = SignedMessage 176 | } 177 | -------------------------------------------------------------------------------- /gimli-hash.lua: -------------------------------------------------------------------------------- 1 | local tests = { 2 | "There's plenty for the both of us, may the best Dwarf win.", 3 | { 4 | 0x54686572, 0x65277320, 0x706c656e, 0x74792066, 0x6f722074, 0x68652062, 0x6f746820, 5 | 0x6f662075, 0x732c206d, 0x61792074, 0x68652062, 0x65737420, 0x44776172, 0x66207769, 6 | 0x6e2e 7 | }, 8 | "4afb3ff784c7ad6943d49cf5da79facfa7c4434e1ce44f5dd4b28f91a84d22c8", 9 | "If anyone was to ask for my opinion, which I note they're not, I'd say we were taking the long way around.", 10 | { 11 | 0x49662061, 0x6e796f6e, 0x65207761, 0x7320746f, 0x2061736b, 0x20666f72, 0x206d7920, 12 | 0x6f70696e, 0x696f6e2c, 0x20776869, 0x63682049, 0x206e6f74, 0x65207468, 0x65792772, 13 | 0x65206e6f, 0x742c2049, 0x27642073, 0x61792077, 0x65207765, 0x72652074, 0x616b696e, 14 | 0x67207468, 0x65206c6f, 0x6e672077, 0x61792061, 0x726f756e, 0x642e 15 | }, 16 | "ba82a16a7b224c15bed8e8bdc88903a4006bc7beda78297d96029203ef08e07c", 17 | "Speak words we can all understand!", 18 | { 19 | 0x53706561, 0x6b20776f, 0x72647320, 0x77652063, 0x616e2061, 0x6c6c2075, 0x6e646572, 20 | 0x7374616e, 0x6421 21 | }, 22 | "8dd4d132059b72f8e8493f9afb86c6d86263e7439fc64cbb361fcbccf8b01267", 23 | "It's true you don't see many Dwarf-women. And in fact, they are so alike in voice and appearance, that they are often mistaken for Dwarf-men. And this in turn has given rise to the belief that there are no Dwarf-women, and that Dwarves just spring out of holes in the ground! Which is, of course, ridiculous.", 24 | { 25 | 0x49742773, 0x20747275, 0x6520796f, 0x7520646f, 0x6e277420, 0x73656520, 0x6d616e79, 26 | 0x20447761, 0x72662d77, 0x6f6d656e, 0x2e20416e, 0x6420696e, 0x20666163, 0x742c2074, 27 | 0x68657920, 0x61726520, 0x736f2061, 0x6c696b65, 0x20696e20, 0x766f6963, 0x6520616e, 28 | 0x64206170, 0x70656172, 0x616e6365, 0x2c207468, 0x61742074, 0x68657920, 0x61726520, 29 | 0x6f667465, 0x6e206d69, 0x7374616b, 0x656e2066, 0x6f722044, 0x77617266, 0x2d6d656e, 30 | 0x2e20416e, 0x64207468, 0x69732069, 0x6e207475, 0x726e2068, 0x61732067, 0x6976656e, 31 | 0x20726973, 0x6520746f, 0x20746865, 0x2062656c, 0x69656620, 0x74686174, 0x20746865, 32 | 0x72652061, 0x7265206e, 0x6f204477, 0x6172662d, 0x776f6d65, 0x6e2c2061, 0x6e642074, 33 | 0x68617420, 0x44776172, 0x76657320, 0x6a757374, 0x20737072, 0x696e6720, 0x6f757420, 34 | 0x6f662068, 0x6f6c6573, 0x20696e20, 0x74686520, 0x67726f75, 0x6e642120, 0x57686963, 35 | 0x68206973, 0x2c206f66, 0x20636f75, 0x7273652c, 0x20726964, 0x6963756c, 0x6f75732e, 36 | }, 37 | "8887a5367d961d6734ee1a0d4aee09caca7fd6b606096ff69d8ce7b9a496cd2f", 38 | "", 39 | {}, 40 | "b0634b2c0b082aedc5c0a2fe4ee3adcfc989ec05de6f00addb04b3aaac271f67" 41 | } 42 | -------------------------------------------------------------------------------- /gimli-permutation.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local bxor = bit.bxor 3 | local rol = bit.rol 4 | local lshift = bit.lshift 5 | local band = bit.band 6 | local bor = bit.bor 7 | 8 | local function gimli(state) 9 | for round = 24,1,-1 do 10 | for column = 0,3,1 do 11 | local x = rol(state[ column], 24) 12 | local y = rol(state[4 + column], 9) 13 | local z = state[8 + column] 14 | 15 | state[8 + column] = bxor(x, lshift(z, 1), lshift(band(y, z), 2)) 16 | state[4 + column] = bxor(y, x, lshift( bor(x, z), 1)) 17 | state[ column] = bxor(z, y, lshift(band(x, y), 3)) 18 | end 19 | 20 | local t = band(round, 3) 21 | if t == 2 then 22 | -- big swap: pattern ..S...S...S. etc. 23 | state[0], state[2] = state[2], state[0] 24 | state[1], state[3] = state[3], state[1] 25 | elseif t == 0 then 26 | -- small swap: pattern s...s...s... etc. 27 | state[0], state[1] = state[1], state[0] 28 | state[2], state[3] = state[3], state[2] 29 | -- add constant: pattern c...c...c... etc. 30 | state[0] = bxor(state[0], bor(0x9e377900, round)) 31 | end 32 | 33 | end 34 | end 35 | 36 | return gimli 37 | 38 | -------------------------------------------------------------------------------- /hex.lua: -------------------------------------------------------------------------------- 1 | return require('base-16')('0123456789abcdef') -------------------------------------------------------------------------------- /ipld.lua: -------------------------------------------------------------------------------- 1 | local p = require "pretty-print".prettyPrint 2 | local Cbor = require "cbor" 3 | local Cid = require "cid" 4 | require 'cid-cbor' 5 | 6 | local a = Cbor.encode { 7 | Cbor.makeTag(42, {"Hello World", name = "Tim", age = 37}), 8 | Cbor.makeTag(55, true) 9 | } 10 | p(a) 11 | -- p("blake2b-256", Cid.link(a, "blake2b-256", "dag-cbor", "z")) 12 | -------------------------------------------------------------------------------- /isutf8.lua: -------------------------------------------------------------------------------- 1 | local find = string.find 2 | 3 | -- Based on http://notebook.kulchenko.com/programming/fixing-malformed-utf8-in-lua 4 | return function (str) 5 | local i = 1 6 | local len = #str 7 | while i <= len do 8 | if i == find(str, '[%z\1-\127]', i) then i = i + 1 9 | elseif i == find(str, '[\194-\223][\128-\191]', i) then i = i + 2 10 | elseif i == find(str, '\224[\160-\191][\128-\191]', i) 11 | or i == find(str, '[\225-\236][\128-\191][\128-\191]', i) 12 | or i == find(str, '\237[\128-\159][\128-\191]', i) 13 | or i == find(str, '[\238-\239][\128-\191][\128-\191]', i) then i = i + 3 14 | elseif i == find(str, '\240[\144-\191][\128-\191][\128-\191]', i) 15 | or i == find(str, '[\241-\243][\128-\191][\128-\191][\128-\191]', i) 16 | or i == find(str, '\244[\128-\143][\128-\191][\128-\191]', i) then i = i + 4 17 | else 18 | return false, i 19 | end 20 | end 21 | return true 22 | end 23 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | local uv = require 'luv' 2 | local ssl = require 'openssl' 3 | 4 | local Switch = require 'switch' 5 | local Multiselect = require 'multiselect' 6 | 7 | local function main() 8 | 9 | local mp = Switch.dial('localhost', 4001) 10 | print 'Connected!' 11 | p(mp.socket:getpeername()) 12 | 13 | local stream = mp.newStream() 14 | Multiselect.negotiate(stream, '/ipfs/ping/1.0.0') 15 | print 'Negotiated mplex ping..' 16 | 17 | local ping = ssl.random(32) 18 | local before = uv.hrtime() 19 | 20 | stream.writeChunk(ping) 21 | assert(stream.readChunk(32) == ping) 22 | local after = uv.hrtime() 23 | print('Ping verified!', after - before .. ' μs') 24 | 25 | print('Closing socket...') 26 | mp.socket:close() 27 | end 28 | 29 | coroutine.wrap(main)() 30 | 31 | uv.run() 32 | -------------------------------------------------------------------------------- /make-callback.lua: -------------------------------------------------------------------------------- 1 | return function() 2 | local thread = coroutine.running() 3 | local done 4 | return function(err, data) 5 | if not done then 6 | done = true 7 | if err then 8 | return assert(coroutine.resume(thread, nil, err)) 9 | else 10 | return assert(coroutine.resume(thread, data or true)) 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /mplex.lua: -------------------------------------------------------------------------------- 1 | local Varint = require 'varint' 2 | local Multiselect = require 'multiselect' 3 | local Connection = require 'connection' 4 | 5 | local NewStream = 0 6 | local MessageReceiver = 1 7 | local MessageInitiator = 2 8 | local CloseReceiver = 3 9 | local CloseInitiator = 4 10 | local ResetReceiver = 5 11 | local ResetInitiator = 6 12 | 13 | local Mplex = {} 14 | 15 | function Mplex.start(masterStream) 16 | Multiselect.negotiate(masterStream, '/mplex/6.7.0') 17 | 18 | local nextId = 0 19 | local dead = false 20 | 21 | local mp = {socket = masterStream.socket} 22 | 23 | local streams = {} 24 | 25 | local function getStream(id) 26 | local stream = streams[id] 27 | if not stream then 28 | stream = Connection.newPush() 29 | streams[id] = stream 30 | end 31 | return stream 32 | end 33 | 34 | local function readLoop() 35 | while true do 36 | local head = Varint.read(masterStream) 37 | if not head then 38 | break 39 | end 40 | local id = head >> 3 41 | local flag = head & 7 42 | local frame = Varint.readFrame(masterStream) 43 | -- p(id, flag, frame) 44 | if flag == MessageReceiver then 45 | getStream(id).onChunk(frame) 46 | end 47 | end 48 | end 49 | coroutine.wrap(readLoop)() 50 | 51 | function mp.newStream(name) 52 | local id = nextId 53 | if not name then 54 | name = '' .. id 55 | end 56 | nextId = nextId + 1 57 | 58 | local stream = getStream(id) 59 | 60 | local function sendFrame(flag, body) 61 | local head = flag | id << 8 62 | masterStream.writeChunk(Varint.encode(head) .. Varint.encode(#body) .. body) 63 | end 64 | 65 | sendFrame(NewStream, name) 66 | 67 | function stream.writeChunk(message) 68 | assert(not dead, 'dead stream') 69 | if message then 70 | sendFrame(MessageInitiator, message) 71 | else 72 | dead = true 73 | sendFrame(CloseInitiator, '') 74 | end 75 | end 76 | 77 | function stream.start() 78 | -- TODO: use for backpressure 79 | end 80 | 81 | function stream.stop() 82 | -- TODO: apply backpressure somehow? 83 | end 84 | 85 | return stream 86 | end 87 | 88 | return mp 89 | end 90 | 91 | return Mplex 92 | -------------------------------------------------------------------------------- /msgframe.lua: -------------------------------------------------------------------------------- 1 | local byte = string.byte 2 | local char = string.char 3 | 4 | -- Frame/deframe messages with simple uint32 headers. 5 | 6 | local Msg = {} 7 | 8 | local function readUint32(data) 9 | return (byte(data, 1) << 24) 10 | | (byte(data, 2) << 16) 11 | | (byte(data, 3) << 8) 12 | | byte(data, 4) 13 | end 14 | 15 | local function encodeUint32(length) 16 | return char( 17 | (0xff & (length >> 24)), 18 | (0xff & (length >> 16)), 19 | (0xff & (length >> 8)), 20 | (0xff & length) 21 | ) 22 | end 23 | 24 | function Msg.writeFrame(stream, message) 25 | return stream.writeChunk(encodeUint32(#message) .. message) 26 | end 27 | 28 | function Msg.readFrame(stream) 29 | local length = readUint32(stream.readChunk(4)) 30 | return stream.readChunk(length) 31 | end 32 | 33 | return Msg 34 | -------------------------------------------------------------------------------- /multibase.lua: -------------------------------------------------------------------------------- 1 | 2 | local function identity() 3 | local function passthrough(message) return message end 4 | return { 5 | encode = passthrough, 6 | decode = passthrough 7 | } 8 | end 9 | 10 | local table = { 11 | {'identity', '\0', identity}, 12 | {'base2', '0', "base-2", '01'}, 13 | {'base8', '7', "base-8", '01234567'}, 14 | {'base10', '9', "base-x", '0123456789'}, 15 | {'base16', 'f', "base-16", '0123456789abcdef'}, 16 | {'base16upper', 'F', "base-16", '0123456789ABCDEF'}, 17 | {'base32', 'b', "base-32", 'abcdefghijklmnopqrstuvwxyz234567'}, 18 | {'base32upper', 'B', "base-32", 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'}, 19 | {'base32pad', 'c', "base-32", 'abcdefghijklmnopqrstuvwxyz234567='}, 20 | {'base32padupper', 'C', "base-32", 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='}, 21 | {'base32hex', 'v', "base-32", '0123456789abcdefghijklmnopqrstuv'}, 22 | {'base32hexupper', 'V', "base-32", '0123456789ABCDEFGHIJKLMNOPQRSTUV'}, 23 | {'base32hexpad', 't', "base-32", '0123456789abcdefghijklmnopqrstuv='}, 24 | {'base32hexpadupper', 'T', "base-32", '0123456789ABCDEFGHIJKLMNOPQRSTUV='}, 25 | {'base32z', 'h', "base-32", 'ybndrfg8ejkmcpqxot1uwisza345h769'}, 26 | {'base58flickr', 'Z', "base-x", '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'}, 27 | {'base58btc', 'z', "base-x", '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'}, 28 | {'base64', 'm', "base-64", 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'}, 29 | {'base64pad', 'M', "base-64", 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='}, 30 | {'base64url', 'u', "base-64", 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'}, 31 | {'base64urlpad', 'U', "base-64", 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='}, 32 | } 33 | 34 | local bases = {} 35 | local codes = {} 36 | local names = {} 37 | for i = 1, #table do 38 | local name, code, fn, alphabet = unpack(table[i]) 39 | bases[name] = { fn, alphabet } 40 | bases[code] = bases[name] 41 | codes[name] = code 42 | codes[code] = code 43 | names[name] = name 44 | names[code] = name 45 | end 46 | 47 | local function getBase(nameOrCode) 48 | local base = assert(bases[nameOrCode], "Unknown name or code") 49 | if type(base[1]) == 'string' then 50 | base[1] = require(base[1]) 51 | end 52 | if type(base[1]) == 'function' then 53 | base = base[1](base[2]) 54 | bases[nameOrCode] = base 55 | end 56 | return base 57 | end 58 | 59 | local function encode(raw, nameOrCode) 60 | local base = getBase(nameOrCode) 61 | local code = codes[nameOrCode] 62 | return code .. base.encode(raw), names[code] 63 | end 64 | 65 | local function decode(encoded) 66 | local code = encoded:sub(1, 1) 67 | local base = getBase(code) 68 | return base.decode(encoded:sub(2)), names[code] 69 | end 70 | 71 | return { 72 | getBase = getBase, 73 | encode = encode, 74 | decode = decode 75 | } -------------------------------------------------------------------------------- /multicodec.lua: -------------------------------------------------------------------------------- 1 | local codecs = { 2 | [0x55] = "raw", -- raw binary 3 | [0x70] = "dag-pb", -- MerkleDAG protobuf 4 | [0x71] = "dag-cbor", -- MerkleDAG cbor 5 | [0x72] = "libp2p-key", -- Libp2p Public Key 6 | [0x78] = "git-raw", -- Raw Git object 7 | [0x7b] = "torrent-info", -- Torrent file info field (bencoded) 8 | [0x7c] = "torrent-file", -- Torrent file (bencoded) 9 | [0x81] = "leofcoin-block", -- Leofcoin Block 10 | [0x82] = "leofcoin-tx", -- Leofcoin Transaction 11 | [0x83] = "leofcoin-pr", -- Leofcoin Peer Reputation 12 | [0x90] = "eth-block", -- Ethereum Block (RLP) 13 | [0x91] = "eth-block-list", -- Ethereum Block List (RLP) 14 | [0x92] = "eth-tx-trie", -- Ethereum Transaction Trie (Eth-Trie) 15 | [0x93] = "eth-tx", -- Ethereum Transaction (RLP) 16 | [0x94] = "eth-tx-receipt-trie", -- Ethereum Transaction Receipt Trie (Eth-Trie) 17 | [0x95] = "eth-tx-receipt", -- Ethereum Transaction Receipt (RLP) 18 | [0x96] = "eth-state-trie", -- Ethereum State Trie (Eth-Secure-Trie) 19 | [0x97] = "eth-account-snapshot", -- Ethereum Account Snapshot (RLP) 20 | [0x98] = "eth-storage-trie", -- Ethereum Contract Storage Trie (Eth-Secure-Trie) 21 | [0xb0] = "bitcoin-block", -- Bitcoin Block 22 | [0xb1] = "bitcoin-tx", -- Bitcoin Tx 23 | [0xc0] = "zcash-block", -- Zcash Block 24 | [0xc1] = "zcash-tx", -- Zcash Tx 25 | [0xd0] = "stellar-block", -- Stellar Block 26 | [0xd1] = "stellar-tx", -- Stellar Tx 27 | [0xe0] = "decred-block", -- Decred Block 28 | [0xe1] = "decred-tx", -- Decred Tx 29 | [0xf0] = "dash-block", -- Dash Block 30 | [0xf1] = "dash-tx", -- Dash Tx 31 | [0xfa] = "swarm-manifest", -- Swarm Manifest 32 | [0xfb] = "swarm-feed", -- Swarm Feed 33 | [0x0129] = "dag-json", -- MerkleDAG json 34 | } 35 | for k, n in pairs(codecs) do 36 | codecs[n] = k 37 | end 38 | return codecs -------------------------------------------------------------------------------- /multihash.lua: -------------------------------------------------------------------------------- 1 | local Varint = require './varint' 2 | 3 | local function identity() 4 | return function (message) return message end 5 | end 6 | 7 | local function sha1() 8 | local hexDecode = require('hex').decode 9 | local hash = require 'sha1' 10 | return function (message) 11 | return hexDecode(hash(message)) 12 | end 13 | end 14 | 15 | local function sha256() 16 | return require('sha256')[256] 17 | end 18 | 19 | local function sha512() 20 | return require('sha512')[512] 21 | end 22 | 23 | local function blake2b(size) 24 | local ffi = require 'ffi' 25 | local rshift = require'bit'.rshift 26 | local hash = require 'blake2b'.hash 27 | local outlen = rshift(size, 3) 28 | return function (message) 29 | return ffi.string(hash(message, outlen), outlen) 30 | end 31 | end 32 | 33 | local function blake2s(size) 34 | local ffi = require 'ffi' 35 | local rshift = require'bit'.rshift 36 | local hash = require 'blake2s'.hash 37 | local outlen = rshift(size, 3) 38 | return function (message) 39 | return ffi.string(hash(message, outlen), outlen) 40 | end 41 | end 42 | 43 | local table = { 44 | {'identity', 0, identity}, 45 | {'sha1', 0x11, sha1}, 46 | {'sha2-256', 0x12, sha256}, 47 | {'sha2-512', 0x13, sha512}, 48 | {"blake2b-8", 0xb201, blake2b, 8}, 49 | {"blake2b-16", 0xb202, blake2b, 16}, 50 | {"blake2b-24", 0xb203, blake2b, 24}, 51 | {"blake2b-32", 0xb204, blake2b, 32}, 52 | {"blake2b-40", 0xb205, blake2b, 40}, 53 | {"blake2b-48", 0xb206, blake2b, 48}, 54 | {"blake2b-56", 0xb207, blake2b, 56}, 55 | {"blake2b-64", 0xb208, blake2b, 64}, 56 | {"blake2b-72", 0xb209, blake2b, 72}, 57 | {"blake2b-80", 0xb20a, blake2b, 80}, 58 | {"blake2b-88", 0xb20b, blake2b, 88}, 59 | {"blake2b-96", 0xb20c, blake2b, 96}, 60 | {"blake2b-104", 0xb20d, blake2b, 104}, 61 | {"blake2b-112", 0xb20e, blake2b, 112}, 62 | {"blake2b-120", 0xb20f, blake2b, 120}, 63 | {"blake2b-128", 0xb210, blake2b, 128}, 64 | {"blake2b-136", 0xb211, blake2b, 136}, 65 | {"blake2b-144", 0xb212, blake2b, 144}, 66 | {"blake2b-152", 0xb213, blake2b, 152}, 67 | {"blake2b-160", 0xb214, blake2b, 160}, 68 | {"blake2b-168", 0xb215, blake2b, 168}, 69 | {"blake2b-176", 0xb216, blake2b, 176}, 70 | {"blake2b-184", 0xb217, blake2b, 184}, 71 | {"blake2b-192", 0xb218, blake2b, 192}, 72 | {"blake2b-200", 0xb219, blake2b, 200}, 73 | {"blake2b-208", 0xb21a, blake2b, 208}, 74 | {"blake2b-216", 0xb21b, blake2b, 216}, 75 | {"blake2b-224", 0xb21c, blake2b, 224}, 76 | {"blake2b-232", 0xb21d, blake2b, 232}, 77 | {"blake2b-240", 0xb21e, blake2b, 240}, 78 | {"blake2b-248", 0xb21f, blake2b, 248}, 79 | {"blake2b-256", 0xb220, blake2b, 256}, 80 | {"blake2b-264", 0xb221, blake2b, 264}, 81 | {"blake2b-272", 0xb222, blake2b, 272}, 82 | {"blake2b-280", 0xb223, blake2b, 280}, 83 | {"blake2b-288", 0xb224, blake2b, 288}, 84 | {"blake2b-296", 0xb225, blake2b, 296}, 85 | {"blake2b-304", 0xb226, blake2b, 304}, 86 | {"blake2b-312", 0xb227, blake2b, 312}, 87 | {"blake2b-320", 0xb228, blake2b, 320}, 88 | {"blake2b-328", 0xb229, blake2b, 328}, 89 | {"blake2b-336", 0xb22a, blake2b, 336}, 90 | {"blake2b-344", 0xb22b, blake2b, 344}, 91 | {"blake2b-352", 0xb22c, blake2b, 352}, 92 | {"blake2b-360", 0xb22d, blake2b, 360}, 93 | {"blake2b-368", 0xb22e, blake2b, 368}, 94 | {"blake2b-376", 0xb22f, blake2b, 376}, 95 | {"blake2b-384", 0xb230, blake2b, 384}, 96 | {"blake2b-392", 0xb231, blake2b, 392}, 97 | {"blake2b-400", 0xb232, blake2b, 400}, 98 | {"blake2b-408", 0xb233, blake2b, 408}, 99 | {"blake2b-416", 0xb234, blake2b, 416}, 100 | {"blake2b-424", 0xb235, blake2b, 424}, 101 | {"blake2b-432", 0xb236, blake2b, 432}, 102 | {"blake2b-440", 0xb237, blake2b, 440}, 103 | {"blake2b-448", 0xb238, blake2b, 448}, 104 | {"blake2b-456", 0xb239, blake2b, 456}, 105 | {"blake2b-464", 0xb23a, blake2b, 464}, 106 | {"blake2b-472", 0xb23b, blake2b, 472}, 107 | {"blake2b-480", 0xb23c, blake2b, 480}, 108 | {"blake2b-488", 0xb23d, blake2b, 488}, 109 | {"blake2b-496", 0xb23e, blake2b, 496}, 110 | {"blake2b-504", 0xb23f, blake2b, 504}, 111 | {"blake2b-512", 0xb240, blake2b, 512}, 112 | {"blake2s-8", 0xb241, blake2s, 8}, 113 | {"blake2s-16", 0xb242, blake2s, 16}, 114 | {"blake2s-24", 0xb243, blake2s, 24}, 115 | {"blake2s-32", 0xb244, blake2s, 32}, 116 | {"blake2s-40", 0xb245, blake2s, 40}, 117 | {"blake2s-48", 0xb246, blake2s, 48}, 118 | {"blake2s-56", 0xb247, blake2s, 56}, 119 | {"blake2s-64", 0xb248, blake2s, 64}, 120 | {"blake2s-72", 0xb249, blake2s, 72}, 121 | {"blake2s-80", 0xb24a, blake2s, 80}, 122 | {"blake2s-88", 0xb24b, blake2s, 88}, 123 | {"blake2s-96", 0xb24c, blake2s, 96}, 124 | {"blake2s-104", 0xb24d, blake2s, 104}, 125 | {"blake2s-112", 0xb24e, blake2s, 112}, 126 | {"blake2s-120", 0xb24f, blake2s, 120}, 127 | {"blake2s-128", 0xb250, blake2s, 128}, 128 | {"blake2s-136", 0xb251, blake2s, 136}, 129 | {"blake2s-144", 0xb252, blake2s, 144}, 130 | {"blake2s-152", 0xb253, blake2s, 152}, 131 | {"blake2s-160", 0xb254, blake2s, 160}, 132 | {"blake2s-168", 0xb255, blake2s, 168}, 133 | {"blake2s-176", 0xb256, blake2s, 176}, 134 | {"blake2s-184", 0xb257, blake2s, 184}, 135 | {"blake2s-192", 0xb258, blake2s, 192}, 136 | {"blake2s-200", 0xb259, blake2s, 200}, 137 | {"blake2s-208", 0xb25a, blake2s, 208}, 138 | {"blake2s-216", 0xb25b, blake2s, 216}, 139 | {"blake2s-224", 0xb25c, blake2s, 224}, 140 | {"blake2s-232", 0xb25d, blake2s, 232}, 141 | {"blake2s-240", 0xb25e, blake2s, 240}, 142 | {"blake2s-248", 0xb25f, blake2s, 248}, 143 | {"blake2s-256", 0xb260, blake2s, 256}, 144 | } 145 | 146 | local hashes = {} 147 | local codes = {} 148 | local names = {} 149 | for i = 1, #table do 150 | local name, code, fn, size = unpack(table[i]) 151 | hashes[name] = { fn, size } 152 | hashes[code] = hashes[name] 153 | codes[name] = code 154 | codes[code] = code 155 | names[name] = name 156 | names[code] = name 157 | end 158 | 159 | local function getHash(nameOrCode) 160 | local hash = assert(hashes[nameOrCode], "Unknown name or code") 161 | if type(hash) == 'table' then 162 | hash = hash[1](hash[2]) 163 | hashes[nameOrCode] = hash 164 | end 165 | return hash 166 | end 167 | 168 | local function encode(digest, nameOrCode) 169 | local length = #digest 170 | local code = assert(codes[nameOrCode], "Unknown name or code") 171 | return Varint.encode(code) .. Varint.encode(length) .. digest, names[code] 172 | end 173 | 174 | local function hash(raw, nameOrCode, length) 175 | local hashfn = getHash(nameOrCode) 176 | local digest = hashfn(raw) 177 | local len = #digest 178 | if not length then length = len end 179 | assert(length <= len, "Specified length longer than natural digest length") 180 | if length < len then 181 | digest = digest:sub(1, length) 182 | end 183 | return encode(digest, nameOrCode) 184 | end 185 | 186 | local function decode(multi, index) 187 | index = index or 1 188 | local code, length 189 | code, index = Varint.decode(multi, index) 190 | length, index = Varint.decode(multi, index) 191 | local last = index + length - 1 192 | assert(#multi >= last) 193 | return multi:sub(index, last), names[code], index 194 | end 195 | 196 | local function verify(raw, multi, index) 197 | index = index or 1 198 | local code, length 199 | code, index = Varint.decode(multi, index) 200 | length, index = Varint.decode(multi, index) 201 | return multi == hash(raw, code, length), index 202 | end 203 | 204 | return { 205 | getHash = getHash, 206 | encode = encode, 207 | decode = decode, 208 | hash = hash, 209 | verify = verify, 210 | } -------------------------------------------------------------------------------- /multiselect.lua: -------------------------------------------------------------------------------- 1 | local Varint = require 'varint' 2 | 3 | local Multiselect = {} 4 | 5 | function Multiselect.negotiate(stream, protocol) 6 | protocol = protocol .. '\n' 7 | Varint.writeFrame(stream, '/multistream/1.0.0\n') 8 | Varint.writeFrame(stream, protocol) 9 | local first = Varint.readFrame(stream) 10 | assert(first == '/multistream/1.0.0\n') 11 | local second = Varint.readFrame(stream) 12 | assert(second == protocol) 13 | end 14 | 15 | return Multiselect 16 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | The multistream docs are out of date https://github.com/multiformats/multistream-select 2 | 3 | When first connecting, the server sends a varint framed message with `/multistream/1.0.0\n` 4 | 5 | We respond with `/multistream/1.0.0\n` and then request `/plaintext/1.0.0\n` and it confirms or rejects. 6 | 7 | Then for some reason it sends `/multistream/1.0.0\n` again? 8 | 9 | Now how do we create a mplex stream? Does `/plaintext/1.0.0` assume mplex without encryption or do we need to do something else? Also is the mplex with it's own length header tunneled inside framed messages or replace the framed multistream messages? 10 | 11 | ---------------------------------------- 12 | ## Multistream and Mplex 13 | 14 | multistream negotiation is used in several places. Once to choose plaintext or encryption, again to choose mplex, and then again inside the tunneled mplex streams I think 15 | 16 | The TCP stream requires you to negotiate either plaintext or secio, depending on how the peer is configured. (default with ipfs-go is secio) 17 | 18 | So, for example to speak plaintext mplex, the conversation looks something like the following (each message is framed with a varint length header) 19 | 20 | - negotiate plaintxt 21 | - Send: `/multistream/1.0.0\n` 22 | - Expect: `/multistream/1.0.0\n` 23 | - Send: `/plaintext/1.0.0\n` 24 | - Expect: `/plaintext/1.0.0\n` 25 | - negotiate mplex 26 | - Send: `/multistream/1.0.0\n` 27 | - Expect: `/multistream/1.0.0\n` 28 | - Send: `/mplex/6.7.0\n` 29 | - Expect: `/mplex/6.7.0\n` 30 | 31 | After this point, the TCP socket is raw mplex messages. These have a slightly different framing: 32 | 33 | Each Message is framed with `varint header`, `varint length`, `message` Where the header contains the flag as the lower 3 bits and the stream ID for the rest of the bits. 34 | 35 | ## Encryption with Secio 36 | 37 | Notice that there was nothing to do between selecting plaintext and selecting mplex. Things are a little more complicated with encryption (aka secio). 38 | 39 | It starts the same, but selecting `secio/1.0.0` instead of `plaintext/1.0.0` 40 | 41 | - negotiate secio 42 | - Send: `/multistream/1.0.0\n` 43 | - Expect: `/multistream/1.0.0\n` 44 | - Send: `/secio/1.0.0\n` 45 | - Expect: `/secio/1.0.0\n` 46 | 47 | But then we need to perform the secio handshake on this transport. It's similar to TLS except there are no root authorities and certificates. 48 | 49 | During secio handshake, framing is no longer using varint headers. Instead it's using simply `uint32_t` length headers (in network byte order) for framing messages. The messages themselves are protobufs, sometimes nested. 50 | 51 | ### Step 1 - Propose cipher suite + send pubkeys + nonce 52 | 53 | In secio, there are three different configuration parameters that both parties need to negotiate. They are: 54 | 55 | - ECDH algorithms for key exchange, `P-256`, `P-384`, and `P-521` are common. These are their NIST curvenames. Typically in openssl, you'll need to know their ASN1 OIDs `prime256v1`, `secp384r1` and `secp521r1`. I want to see if I can add `Curve25519` as well. 56 | - Cipher to use for actual encryption. Common options are `AES-256`, `AES-128` and `Blowfish`. I want to add something `Gimli` based. 57 | - hash used for MAC, common hash algorithms are `SHA256` and `SHA512`. I want to also use `Gimli` for this. 58 | - Each peer needs to also us a public key for signing part of the handshake and optionally authenticating itself. This key tends to be `RSA-2048`. I want to use `ED25519` for faster ephermeral key generation. 59 | 60 | *Basically, I want to be able to write embedded clients using https://github.com/jedisct1/libhydrogen which is why I want newer options added.* 61 | 62 | The propose [message is a protobuf](https://github.com/libp2p/go-libp2p-secio/blob/master/pb/spipe.proto) with [another embedded inside](https://github.com/libp2p/go-libp2p-crypto/blob/master/pb/crypto.proto). 63 | 64 | - `rand` - 16 random bytes. (Used later for tie breaking in suite resolving algorithm and for extra entropy) 65 | - `pubkey` Which itself is protobuf encoded sub-object: 66 | - `type` - enum for type (typically `KeyEnum.RSA`) 67 | - `data` - RSA key encoded as binary der 68 | - `exchanges` - Comma separated options like `P-256,P-384,P-521` 69 | - `ciphers` - Comma separated options like `AES-256,AES-128,Blowfish` 70 | - `hashes` - Comma separated options like `SHA256,SHA512` 71 | 72 | Here are the relevent protobuf definitions: 73 | 74 | ```protobuf 75 | message Propose { 76 | optional bytes rand = 1; 77 | optional bytes pubkey = 2; 78 | optional string exchanges = 3; 79 | optional string ciphers = 4; 80 | optional string hashes = 5; 81 | } 82 | 83 | enum KeyType { 84 | RSA = 0; 85 | Ed25519 = 1; 86 | Secp256k1 = 2; 87 | ECDSA = 3; 88 | } 89 | 90 | message PublicKey { 91 | required KeyType Type = 1; 92 | required bytes Data = 2; 93 | } 94 | ``` 95 | 96 | 97 | The order is chosen based on a hash of the 16 random bytes and the public key. This makes it hard for a peer to have priority and is essentially a deterministic random leader. 98 | 99 | The public key of the peer (still in binary format) concated with our own random bytes (binary) runs through SHA256. The same is done for the other side (our public key and their random bits) 100 | 101 | These two hashes are then compared to know the order when choosing the best option 102 | 103 | ```lua 104 | order = SHA256(inProp.key + outProp.rand) > SHA256(outProp.key, inProp.rand) 105 | ``` 106 | 107 | if `order` is true then own proposals have priority, otherwise, peer's proposals go in the outer loop. 108 | 109 | 110 | ### Step 2 - Perform ECDH exchange and verify signatures 111 | 112 | Once you know the 113 | 114 | ### Step 3 - Send expected messages to verify encryption is working. 115 | 116 | 117 | ## Annotated Conversation 118 | 119 | Sometimes seeing the actual bytes in an example is the best way to understand. 120 | 121 | ```sh 122 | # 19 is varint length header 123 | out> [13] '/multistream/1.0.0\n' 124 | # 13 is varint length header 125 | out> [0d] '/secio/1.0.0\n' 126 | # Peer sends back same messages if it agrees 127 | in> [13] '/multistream/1.0.0\n' 128 | in> [0d] '/secio/1.0.0\n' 129 | # Send length header for propose message 130 | out> [00 00 01 6c] # 364 encoded as network order U32 for length header framing. 131 | # See https://developers.google.com/protocol-buffers/docs/encoding for protobuf parts 132 | # rand is 16 random bytes 133 | 00001 010 # Field 1 type length-delimited (1 byte varint and then result is split into bits) 134 | 0 0010000 # varint for length (16 bytes) 135 | [7c 90 e1 67 8c f0 5e cd 63 fc db 8b 34 e8 1f f5] # 16 random bytes 136 | # pubkey is nested inline protobuf, so in outer protobuf it's just opaque bytes. 137 | 00010 010 # Field 2 type length-delimited 138 | 1 0101011 0 0000010 # varint length header (decodes to 0000010 0101011 which is 299) 139 | 140 | # Rest of message to parse 141 | [ab 2 8 0 12 a6 2 30 82 1 22 30 d 6 9 2a 86 48 86 f7 d 1 1 1 5 0 3 82 1 f 0 30 82 1 a 2 82 1 1 0 d6 2f bf 96 aa 27 ba c0 ef d8 9a 5c 24 79 a6 8a df b6 20 b1 a2 b4 60 60 a2 c4 16 be c7 5c 10 8e 32 e7 9a 5 66 9f 90 29 9d a5 1f 1c c9 23 f8 2 47 fa 8a da 68 1b 9c b7 ab 65 ca a7 b1 b0 23 d0 17 48 a6 19 61 43 69 55 77 20 e3 7a b1 45 ef b1 b1 ab d6 eb 98 92 a1 a0 9a a5 ae 94 48 4b ae bb 8e fc ce 60 59 d2 52 6c d0 9 96 5 7e b3 e1 b8 cf 94 1c 3e d7 ff bd 90 a8 96 4b 3d b3 56 10 18 5f 48 b a6 35 16 c4 a5 3c c4 c 36 11 a3 87 b2 45 a4 c2 b9 cd 2b 15 c6 b a3 36 d9 8d f6 5 43 8b 72 1d 9a 7c a6 80 ce 5 d9 d4 1d 11 83 8d 34 54 33 fb b3 42 45 ab 40 67 c 24 47 fe a3 87 7 ec 41 dc eb 2f c4 fc 94 fd 8a bc bb 6e 31 58 89 6 5 c1 cb 46 12 41 6f 1f 28 ed 63 e0 e7 76 fc ad 38 96 49 9a 21 12 28 f0 41 d0 ce 77 44 ae a1 3a 2f b2 7d 4c 87 cb 96 1f 20 18 e1 5a 5d 5a 59 2 3 1 142 | 0 1 1a 11 50 2d 32 35 36 2c 50 2d 33 38 34 2c 50 2d 35 32 31 22 8 42 6c 6f 77 66 69 73 68 2a d 53 48 41 32 35 36 2c 53 48 41 35 31 32] 143 | -------------------------------------------------------------------------------- /old/buffer.lua: -------------------------------------------------------------------------------- 1 | return require'ffi'.typeof'uint8_t[?]' 2 | -------------------------------------------------------------------------------- /old/package.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "filecoin-project/lua-filecoin", 3 | version = "0.0.1", 4 | description = "Prototype of Filecoin in Lua", 5 | tags = { "filecoin", "p2p" }, 6 | luvi = { 7 | inline = "#!/usr/local/bin/luvi --\n" 8 | }, 9 | license = "MIT/Apache", 10 | author = { name = "Tim Caswell", email = "tim.caswell@protocol.ai" }, 11 | homepage = "https://github.com/filecoin-project/lua-filecoin", 12 | dependencies = { 13 | "luvit/pretty-print", 14 | "creationix/uv", 15 | "creationix/cbor", 16 | }, 17 | files = { 18 | "**.lua", 19 | "!unused*", 20 | "!test*" 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /old/uv.lua: -------------------------------------------------------------------------------- 1 | --[[lit-meta 2 | name = "creationix/uv" 3 | description = "Shim to use 'luv' as 'uv' for non-luvi based apps using luvit libs" 4 | version = "1.8.0" 5 | ]] 6 | return require 'luv' 7 | -------------------------------------------------------------------------------- /pretty-print.lua: -------------------------------------------------------------------------------- 1 | local getenv = require('os').getenv 2 | local isUtf8 = require 'isutf8' 3 | local ffi = require 'ffi' 4 | 5 | local prettyPrint, dump, strip, color, colorize, loadColors 6 | local theme = {} 7 | local useColors = false 8 | local defaultTheme 9 | 10 | local width 11 | 12 | local quote, quote2, dquote, dquote2, bquote, bquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls 13 | 14 | local themes = { 15 | -- nice color theme using 16 ansi colors 16 | [16] = { 17 | property = "0;37", -- white 18 | sep = "1;30", -- bright-black 19 | braces = "1;30", -- bright-black 20 | 21 | ["nil"] = "1;30", -- bright-black 22 | boolean = "0;33", -- yellow 23 | number = "1;33", -- bright-yellow 24 | string = "0;32", -- green 25 | quotes = "1;32", -- bright-green 26 | escape = "1;32", -- bright-green 27 | ["function"] = "0;35", -- purple 28 | thread = "1;35", -- bright-purple 29 | 30 | table = "1;34", -- bright blue 31 | userdata = "1;36", -- bright cyan 32 | cdata = "0;36", -- cyan 33 | 34 | err = "1;31", -- bright red 35 | success = "1;33;42", -- bright-yellow on green 36 | failure = "1;33;41", -- bright-yellow on red 37 | highlight = "1;36;44", -- bright-cyan on blue 38 | }, 39 | -- nice color theme using ansi 256-mode colors 40 | [256] = { 41 | property = "38;5;253", 42 | braces = "38;5;247", 43 | sep = "38;5;240", 44 | 45 | ["nil"] = "38;5;244", 46 | boolean = "38;5;220", -- yellow-orange 47 | number = "38;5;202", -- orange 48 | string = "38;5;34", -- darker green 49 | quotes = "38;5;40", -- green 50 | escape = "38;5;46", -- bright green 51 | ["function"] = "38;5;129", -- purple 52 | thread = "38;5;199", -- pink 53 | 54 | table = "38;5;27", -- blue 55 | userdata = "38;5;39", -- blue2 56 | cdata = "38;5;69", -- teal 57 | 58 | err = "38;5;196", -- bright red 59 | success = "38;5;120;48;5;22", -- bright green on dark green 60 | failure = "38;5;215;48;5;52", -- bright red on dark red 61 | highlight = "38;5;45;48;5;236", -- bright teal on dark grey 62 | }, 63 | } 64 | 65 | local special = { 66 | [7] = 'a', 67 | [8] = 'b', 68 | [9] = 't', 69 | [10] = 'n', 70 | [11] = 'v', 71 | [12] = 'f', 72 | [13] = 'r' 73 | } 74 | 75 | function strip(str) 76 | return string.gsub(str, '\027%[[^m]*m', '') 77 | end 78 | 79 | function loadColors(index) 80 | if index == nil then index = defaultTheme end 81 | 82 | -- Remove the old theme 83 | for key in pairs(theme) do 84 | theme[key] = nil 85 | end 86 | 87 | if index then 88 | local new = themes[index] 89 | if not new then error("Invalid theme index: " .. tostring(index)) end 90 | -- Add the new theme 91 | for key in pairs(new) do 92 | theme[key] = new[key] 93 | end 94 | useColors = true 95 | else 96 | useColors = false 97 | end 98 | 99 | quote = colorize('quotes', "'", 'string') 100 | quote2 = colorize('quotes', "'") 101 | dquote = colorize('quotes', '"', 'string') 102 | dquote2 = colorize('quotes', '"') 103 | bquote = colorize('quotes', '<', 'string') 104 | bquote2 = colorize('quotes', '>') 105 | obrace = colorize('braces', '{ ') 106 | cbrace = colorize('braces', '}') 107 | obracket = colorize('property', '[') 108 | cbracket = colorize('property', ']') 109 | comma = colorize('sep', ', ') 110 | equals = colorize('sep', ' = ') 111 | 112 | controls = {} 113 | for i = 0, 31 do 114 | local c = special[i] 115 | if not c then 116 | c = string.format("x%02x", i) 117 | end 118 | controls[i] = colorize('escape', '\\' .. c, 'string') 119 | end 120 | controls[92] = colorize('escape', '\\\\', 'string') 121 | controls[34] = colorize('escape', '\\"', 'string') 122 | controls[39] = colorize('escape', "\\'", 'string') 123 | for i = 128, 255 do 124 | local c = string.format("x%02x", i) 125 | controls[i] = colorize('escape', '\\' .. c, 'string') 126 | end 127 | end 128 | 129 | function color(colorName) 130 | return '\27[' .. (theme[colorName] or '0') .. 'm' 131 | end 132 | 133 | function colorize(colorName, string, resetName) 134 | return useColors and 135 | (color(colorName) .. tostring(string) .. color(resetName)) or 136 | tostring(string) 137 | end 138 | 139 | local function stringEscape(c) 140 | return controls[string.byte(c, 1)] 141 | end 142 | 143 | local utils = {} 144 | local tempignores = {} 145 | 146 | function dump(value, recurse, nocolor) 147 | local seen = {} 148 | local output = {} 149 | local offset = 0 150 | local stack = {} 151 | 152 | local function recalcOffset(index) 153 | for i = index + 1, #output do 154 | local m = string.match(output[i], "\n([^\n]*)$") 155 | if m then 156 | offset = #(strip(m)) 157 | else 158 | offset = offset + #(strip(output[i])) 159 | end 160 | end 161 | end 162 | 163 | local function write(text, length) 164 | if not length then length = #(strip(text)) end 165 | -- Create room for data by opening parent blocks 166 | -- Start at the root and go down. 167 | local i = 1 168 | while offset + length > width and stack[i] do 169 | local entry = stack[i] 170 | if not entry.opened then 171 | entry.opened = true 172 | table.insert(output, entry.index + 1, "\n" .. string.rep(" ", i)) 173 | -- Recalculate the offset 174 | recalcOffset(entry.index) 175 | -- Bump the index of all deeper entries 176 | for j = i + 1, #stack do 177 | stack[j].index = stack[j].index + 1 178 | end 179 | end 180 | i = i + 1 181 | end 182 | output[#output + 1] = text 183 | offset = offset + length 184 | if offset > width then 185 | return dump(stack) 186 | end 187 | end 188 | 189 | local function indent() 190 | stack[#stack + 1] = { 191 | index = #output, 192 | opened = false, 193 | } 194 | end 195 | 196 | local function unindent() 197 | stack[#stack] = nil 198 | end 199 | 200 | local function process(localValue) 201 | local typ = type(localValue) 202 | if typ == 'string' then 203 | if isUtf8(localValue) then 204 | if string.find(localValue, "'") and not string.find(localValue, '"') then 205 | write(dquote) 206 | write(string.gsub(localValue, '[%c\\\128-\255]', stringEscape)) 207 | write(dquote2) 208 | else 209 | write(quote) 210 | write(string.gsub(localValue, "[%c\\'\128-\255]", stringEscape)) 211 | write(quote2) 212 | end 213 | else 214 | write(bquote) 215 | write(localValue:gsub('.', function (c) 216 | return string.format("%02x", c:byte(1)) 217 | end)) 218 | write(bquote2) 219 | end 220 | elseif typ == 'table' and not seen[localValue] then 221 | if not recurse then seen[localValue] = true end 222 | local meta = getmetatable(localValue) 223 | if meta and meta.tag then 224 | write(colorize('highlight', meta.tag)) 225 | end 226 | write(obrace) 227 | local i = 1 228 | -- Count the number of keys so we know when to stop adding commas 229 | local total = 0 230 | for _ in pairs(localValue) do total = total + 1 end 231 | 232 | local nextIndex = 1 233 | for k, v in pairs(localValue) do 234 | indent() 235 | if k == nextIndex then 236 | -- if the key matches the last numerical index + 1 237 | -- This is how lists print without keys 238 | nextIndex = k + 1 239 | process(v) 240 | else 241 | if type(k) == "string" and string.find(k, "^[%a_][%a%d_]*$") then 242 | write(colorize("property", k)) 243 | write(equals) 244 | else 245 | write(obracket) 246 | process(k) 247 | write(cbracket) 248 | write(equals) 249 | end 250 | if type(v) == "table" then 251 | process(v) 252 | else 253 | indent() 254 | process(v) 255 | unindent() 256 | end 257 | end 258 | if i < total then 259 | write(comma) 260 | else 261 | write(" ") 262 | end 263 | i = i + 1 264 | unindent() 265 | end 266 | write(cbrace) 267 | elseif typ == 'cdata' then 268 | write(colorize(typ, tostring(localValue) .. ':' .. ffi.sizeof(localValue))) 269 | else 270 | write(colorize(typ, tostring(localValue))) 271 | end 272 | end 273 | 274 | process(value) 275 | local s = table.concat(output) 276 | return nocolor and strip(s) or s 277 | end 278 | 279 | utils.colorize = colorize 280 | utils.dump = dump 281 | 282 | function prettyPrint(...) 283 | local n = select('#', ...) 284 | local arguments = {...} 285 | for i = 1, n do 286 | arguments[i] = dump(arguments[i]) 287 | end 288 | print(unpack(arguments)) 289 | end 290 | 291 | width = 80 292 | -- auto-detect when 16 color mode should be used 293 | local term = getenv("TERM") 294 | if term and (term == 'xterm' or term:find'-256color$') then 295 | defaultTheme = 256 296 | else 297 | defaultTheme = 16 298 | end 299 | 300 | loadColors() 301 | 302 | _G.p = prettyPrint 303 | 304 | return { 305 | loadColors = loadColors, 306 | theme = theme, 307 | prettyPrint = prettyPrint, 308 | dump = dump, 309 | color = color, 310 | colorize = colorize, 311 | strip = strip, 312 | } 313 | -------------------------------------------------------------------------------- /protobuf.lua: -------------------------------------------------------------------------------- 1 | local sub = string.sub 2 | local Varint = require 'varint' 3 | 4 | local Protobuf = {} 5 | 6 | local function encodeValue(fieldNumber, val) 7 | if not val then 8 | return '' 9 | end 10 | local typ = type(val) 11 | if typ == 'string' then 12 | local key = 2 | fieldNumber << 3 13 | local len = #val 14 | return table.concat { 15 | Varint.encode(key), 16 | Varint.encode(len), 17 | val 18 | } 19 | elseif typ == 'number' then 20 | local key = fieldNumber << 3 21 | return table.concat { 22 | Varint.encode(key), 23 | Varint.encode(val) 24 | } 25 | end 26 | end 27 | 28 | local function decodeValue(data, index) 29 | local key, len 30 | key, index = Varint.decode(data, index) 31 | local fieldNumber = key >> 3 32 | local typ = key & 7 33 | local val 34 | if typ == 0 then 35 | val, index = Varint.decode(data, index) 36 | elseif typ == 1 then 37 | error('TODO: implement 64bit decoder') 38 | elseif typ == 2 then 39 | len, index = Varint.decode(data, index) 40 | val = sub(data, index, index + len - 1) 41 | index = index + len 42 | elseif typ == 5 then 43 | error('TODO: implement 32bit decoder') 44 | else 45 | error('Unexpected protobuf type: ' .. typ) 46 | end 47 | return fieldNumber, val, index 48 | end 49 | 50 | -- For now assume all values are strings 51 | function Protobuf.encodeTable(schema, val) 52 | local parts = {} 53 | for i = 1, #schema do 54 | parts[i] = encodeValue(i, val[schema[i]]) 55 | end 56 | return table.concat(parts) 57 | end 58 | 59 | function Protobuf.decodeTable(schema, data) 60 | local val = {} 61 | local index = 1 62 | local len = #data 63 | while index <= len do 64 | local fieldNumber, str 65 | fieldNumber, str, index = decodeValue(data, index) 66 | val[schema[fieldNumber] or fieldNumber] = str 67 | end 68 | return val 69 | end 70 | 71 | return Protobuf 72 | -------------------------------------------------------------------------------- /secio.lua: -------------------------------------------------------------------------------- 1 | local Protobuf = require 'protobuf' 2 | local Msg = require 'msgframe' 3 | local prettyPrint = require 'pretty-print' 4 | 5 | local pkey = require 'openssl/pkey' 6 | local digest = require 'openssl/digest' 7 | local hmac = require 'openssl/hmac' 8 | local rand = require 'openssl/rand' 9 | local Exchange = require 'exchange' 10 | 11 | local Connection = require 'connection' 12 | 13 | -- local C = ffi.load'ssl' 14 | -- ffi.cdef [[ 15 | -- typedef struct file_t FILE; 16 | -- FILE *fopen(const char *filename, const char *mode); 17 | -- int EC_KEY_print_fp(FILE *fp, const EC_KEY *key, int off); 18 | -- int BIO_dump_fp(FILE *fp, const char *s, int len); 19 | -- int BIO_dump_indent_fp(FILE *fp, const char *s, int len, int indent); 20 | -- 21 | -- ]] 22 | 23 | --local stdout = ffi.C.fopen('/dev/stdout', 'w') 24 | 25 | --local function log(key) 26 | -- C.EC_KEY_print_fp(stdout, key, 2) 27 | --end 28 | 29 | local function dump(label, str, indent) 30 | indent = indent or 0 31 | print(string.rep(' ', indent) .. prettyPrint.colorize('highlight', label) .. ':') 32 | indent = indent + 2 33 | local typ = type(str) 34 | if typ == 'string' then 35 | p(str)--C.BIO_dump_indent_fp(stdout, str, #str, indent) 36 | elseif typ == 'table' then 37 | for k, v in pairs(str) do 38 | dump(k, v, indent) 39 | end 40 | return 41 | else 42 | print(string.rep(' ', indent) .. prettyPrint.dump(str)) 43 | end 44 | end 45 | 46 | local Secio = {} 47 | 48 | local SupportedExchanges = 'P-256,P-384,P-521' 49 | -- local SupportedCiphers = 'AES-256,AES-128,Blowfish' 50 | local SupportedCiphers = 'AES-256,AES-128' 51 | local SupportedHashes = 'SHA256,SHA512' 52 | 53 | local ProposeSchema = { 54 | 'rand', 55 | 'pubkey', 56 | 'exchanges', 57 | 'ciphers', 58 | 'hashes' 59 | } 60 | 61 | local ExchangeSchema = { 62 | 'epubkey', 63 | 'signature' 64 | } 65 | 66 | local PublicKeySchema = { 67 | 'type', 68 | 'data' 69 | } 70 | 71 | local KeyEnum = { 72 | [0] = 'RSA', 73 | [1] = 'Ed25519', 74 | [2] = 'Secp256k1', 75 | [3] = 'ECDSA', 76 | RSA = 0, 77 | Ed25519 = 1, 78 | Secp256k1 = 2, 79 | ECDSA = 3 80 | } 81 | 82 | local function selectBest(order, set1, set2) 83 | if order == 0 then 84 | return set1:match('([^,]+)') 85 | end 86 | if order < 0 then 87 | set2, set1 = set1, set2 88 | end 89 | for first in set1:gmatch('([^,]+)') do 90 | for second in set2:gmatch('([^,]+)') do 91 | if first == second then 92 | return first 93 | end 94 | end 95 | end 96 | end 97 | 98 | -- Sanity checks for selectBest algorithm 99 | assert(selectBest(0, 'zed', 'three,two,one') == 'zed') 100 | assert(selectBest(1, 'one,two,three', 'three,two,one') == 'one') 101 | assert(selectBest(-1, 'one,two,three', 'three,two,one') == 'three') 102 | assert(selectBest(1, 'one', 'three,two,one') == 'one') 103 | assert(selectBest(1, 'two,three', 'one,three,two') == 'two') 104 | assert(selectBest(1, '5,4,3', '1,2,3') == '3') 105 | assert(selectBest(-1, '5,4,3', '1,2,3') == '3') 106 | assert(selectBest(-1, 'two,three', 'one,three,two') == 'three') 107 | assert(selectBest(-1, 'one', 'three,two,one') == 'one') 108 | 109 | local CipherMap = { 110 | ['AES-128'] = { 111 | ivSize = 16, 112 | keySize = 16 113 | }, 114 | ['AES-256'] = { 115 | ivSize = 16, 116 | keySize = 32 117 | }, 118 | ['Blowfish'] = { 119 | ivSize = 8, 120 | keySize = 32 121 | } 122 | } 123 | 124 | -- generate two sets of keys (stretching) 125 | -- cipher is cipher type (AES-128, AES-256, BlowFish) 126 | -- hash is hash type (SHA256, SHA512) 127 | -- secret is shared secret from ECDH exchange 128 | -- output is sets of keys 129 | local function keyStretcher(cipherType, hashType, secret) 130 | local sizes = assert(CipherMap[cipherType], 'Unsupported ciphertype') 131 | local ivSize = sizes.ivSize 132 | local keySize = sizes.keySize 133 | 134 | local hmacKeySize = 20 135 | local seed = 'key expansion' 136 | local resultLength = 2 * (ivSize + keySize + hmacKeySize) 137 | 138 | local a = hmac.digest(hashType, seed, secret, true) 139 | local parts = {} 140 | local left = resultLength 141 | local i = 0 142 | while left > 0 do 143 | i = i + 1 144 | local b = hmac.digest(hashType, table.concat {a, seed}, secret, true) 145 | parts[i] = b 146 | left = left - #b 147 | a = hmac.digest(hashType, a, secret, true) 148 | end 149 | local result = table.concat(parts) 150 | local half = resultLength / 2 151 | return { 152 | iv = result:sub(1, ivSize), 153 | macKey = result:sub(ivSize + keySize + 1, ivSize + keySize + hmacKeySize), 154 | key = result:sub(ivSize + 1, ivSize + keySize) 155 | }, { 156 | iv = result:sub(half + 1, half + ivSize), 157 | macKey = result:sub(half + ivSize + keySize + 1, half + ivSize + keySize + hmacKeySize), 158 | key = result:sub(half + ivSize + 1, half + ivSize + keySize) 159 | } 160 | end 161 | --[[ 162 | ffi.cdef [[ 163 | typedef struct evp_cipher_st EVP_CIPHER; 164 | typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; 165 | typedef struct engine_st ENGINE; 166 | 167 | EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); 168 | int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx); 169 | void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx); 170 | 171 | int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 172 | ENGINE *impl, const unsigned char *key, const unsigned char *iv); 173 | int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 174 | int *outl, const unsigned char *in, int inl); 175 | int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, 176 | int *outl); 177 | 178 | int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 179 | ENGINE *impl, const unsigned char *key, const unsigned char *iv); 180 | int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 181 | int *outl, const unsigned char *in, int inl); 182 | int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); 183 | 184 | 185 | const EVP_CIPHER *EVP_aes_128_ctr(void); 186 | const EVP_CIPHER *EVP_aes_256_ctr(void); 187 | 188 | /* 189 | typedef struct evp_md_st EVP_MD; 190 | 191 | int EVP_Digest(const void *data, size_t count, 192 | unsigned char *md, unsigned int *size, 193 | const EVP_MD *type, ENGINE *impl); 194 | 195 | const EVP_MD *EVP_sha256(void); 196 | const EVP_MD *EVP_sha512(void); 197 | 198 | unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len, 199 | const unsigned char *d, size_t n, unsigned char *md, 200 | unsigned int *md_len); 201 | */ 202 | ]] 203 | --]] 204 | 205 | local function makeDigest(hashType, seed) 206 | -- Setup the hash 207 | -- local digestType 208 | -- local digestSize 209 | -- if hashType == 'SHA256' then 210 | -- digestType = C.EVP_sha256() 211 | -- digestSize = 32 212 | -- elseif hashType == 'SHA512' then 213 | -- digestType = C.EVP_sha512() 214 | -- digestSize = 64 215 | -- else 216 | -- error('Unsupported encryption ciphertype: ' .. hashType) 217 | -- end 218 | return function(message) 219 | -- dump('hashType', hashType) 220 | -- dump('seed', seed) 221 | -- dump('message', message) 222 | return hmac.digest(hashType, message, seed, true) 223 | end 224 | end 225 | 226 | local function makeEncrypt(cipherType, iv, key) 227 | -- Create and initialise the context 228 | local ctx = C.EVP_CIPHER_CTX_new() 229 | assert(ctx ~= nil) 230 | 231 | -- Setup the cipher 232 | if cipherType == 'AES-128' then 233 | assert(1 == C.EVP_EncryptInit_ex(ctx, C.EVP_aes_128_ctr(), nil, key, iv)) 234 | elseif cipherType == 'AES-256' then 235 | assert(1 == C.EVP_EncryptInit_ex(ctx, C.EVP_aes_256_ctr(), nil, key, iv)) 236 | else 237 | error('Unsupported encryption ciphertype: ' .. cipherType) 238 | end 239 | 240 | local alive = true 241 | 242 | return function(plainText) 243 | assert(alive) 244 | if not plainText then 245 | C.EVP_CIPHER_CTX_free(ctx) 246 | alive = false 247 | return 248 | end 249 | 250 | local plainLen = #plainText 251 | local cipherText = newBuffer(plainLen) 252 | local len = ffi.new 'int[1]' 253 | assert(1 == C.EVP_EncryptUpdate(ctx, cipherText, len, plainText, plainLen)) 254 | return ffi.string(cipherText, len[0]) 255 | end 256 | end 257 | 258 | local function makeDecrypt(cipherType, iv, key) 259 | -- Create and initialise the context 260 | local ctx = C.EVP_CIPHER_CTX_new() 261 | assert(ctx ~= nil) 262 | 263 | -- Setup the cipher 264 | if cipherType == 'AES-128' then 265 | assert(1 == C.EVP_DecryptInit_ex(ctx, C.EVP_aes_128_ctr(), nil, key, iv)) 266 | elseif cipherType == 'AES-256' then 267 | assert(1 == C.EVP_EncryptInit_ex(ctx, C.EVP_aes_256_ctr(), nil, key, iv)) 268 | else 269 | error('Unsupported decryption ciphertype: ' .. cipherType) 270 | end 271 | 272 | local alive = true 273 | return function(cipherText) 274 | assert(alive) 275 | if not cipherText then 276 | C.EVP_CIPHER_CTX_free(ctx) 277 | alive = false 278 | return 279 | end 280 | 281 | local cipherLen = #cipherText 282 | local plainText = newBuffer(cipherLen) 283 | local len = ffi.new 'int[1]' 284 | assert(1 == C.EVP_DecryptUpdate(ctx, plainText, len, cipherText, cipherLen)) 285 | return ffi.string(plainText, len[0]) 286 | end 287 | end 288 | 289 | -- [4 bytes len(therest)][ cipher(data) ][ H(cipher(data)) ] 290 | -- CTR mode AES 291 | local function wrapStream(stream, cipherType, hashType, k1, k2) 292 | local encrypt = makeEncrypt(cipherType, k1.iv, k1.key) 293 | local decrypt = makeDecrypt(cipherType, k2.iv, k2.key) 294 | local mac1 = makeDigest(hashType, k1.macKey) 295 | local mac2 = makeDigest(hashType, k2.macKey) 296 | local hashSize = hashType == 'SHA256' and 32 or 64 297 | 298 | local function readNext() 299 | local frame = Msg.readFrame(stream) 300 | if not frame then 301 | return 302 | end 303 | local length = #frame 304 | local cipher = frame:sub(1, length - hashSize) 305 | local hash = frame:sub(length - hashSize + 1, length) 306 | assert(mac2(cipher) == hash, 'digest mismatch') 307 | local chunk = decrypt(cipher) 308 | dump('reading', chunk) 309 | return chunk 310 | end 311 | 312 | local function writeChunk(chunk) 313 | dump('writing', chunk) 314 | if not chunk then 315 | stream.writeChunk() 316 | return 317 | end 318 | local encrypted = encrypt(chunk) 319 | local hash = mac1(encrypted) 320 | Msg.writeFrame(stream, encrypted .. hash) 321 | end 322 | 323 | local readByte, readChunk = Connection.wrapRead(readNext) 324 | 325 | return { 326 | readByte = readByte, 327 | readChunk = readChunk, 328 | writeChunk = writeChunk, 329 | socket = stream.socket 330 | } 331 | end 332 | 333 | function Secio.wrap(stream) 334 | ---------------------------------------------------------------------------- 335 | -- step 1. Propose -- propose cipher suite + send pubkeys + nonce 336 | -- 337 | -- Generate and send Hello packet. 338 | -- Hello = (rand, PublicKey, Supported) 339 | 340 | local key1 = pkey.new{ bits = 2048 } 341 | local proposeOut = { 342 | rand = rand.bytes(16), 343 | pubkey = Protobuf.encodeTable( 344 | PublicKeySchema, 345 | { 346 | type = KeyEnum.RSA, 347 | data = key1:tostring('der') 348 | } 349 | ), 350 | exchanges = SupportedExchanges, 351 | ciphers = SupportedCiphers, 352 | hashes = SupportedHashes 353 | } 354 | dump('proposeOut', proposeOut) 355 | local proposeOutBytes = Protobuf.encodeTable(ProposeSchema, proposeOut) 356 | Msg.writeFrame(stream, proposeOutBytes) 357 | 358 | -- Receive and parse their Propose packet 359 | local proposeInBytes = Msg.readFrame(stream) 360 | local proposeIn = Protobuf.decodeTable(ProposeSchema, proposeInBytes) 361 | dump('proposeIn', proposeIn) 362 | 363 | -------------------------------------------------------------------------- 364 | -- step 1.1 Identify -- get identity from their key 365 | local skey = Protobuf.decodeTable(PublicKeySchema, proposeIn.pubkey) 366 | assert(skey.type == KeyEnum.RSA, 'Expected RSA key from server') 367 | local key2 = assert(pkey.new(skey.data, 'der')) 368 | -- TODO: do rest of get identity... 369 | 370 | ---------------------------------------------------------------------------- 371 | -- step 1.2 Selection -- select/agree on best encryption parameters 372 | 373 | -- to determine order, use cmp(H(remote_pubkey||local_rand), H(local_pubkey||remote_rand)). 374 | local oh1 = digest.new('sha256') 375 | :update(proposeIn.pubkey) 376 | :final(proposeOut.rand) 377 | local oh2 = digest.new('sha256') 378 | :update(proposeOut.pubkey) 379 | :final(proposeIn.rand) 380 | assert(oh1 ~= oh2) -- talking to self (same socket. must be reuseport + dialing self) 381 | local order = oh1 > oh2 and 1 or -1 382 | 383 | -- we use the same params for both directions (must choose same curve) 384 | -- WARNING: if they dont SelectBest the same way, this won't work... 385 | local exchange = selectBest(order, proposeOut.exchanges, proposeIn.exchanges) 386 | local cipher = selectBest(order, proposeOut.ciphers, proposeIn.ciphers) 387 | local hash = selectBest(order, proposeOut.hashes, proposeIn.hashes) 388 | 389 | dump( 390 | 'config', 391 | { 392 | exchange = exchange, 393 | cipher = cipher, 394 | hash = hash 395 | } 396 | ) 397 | 398 | ---------------------------------------------------------------------------- 399 | -- step 2. Exchange -- exchange (signed) ephemeral keys. verify signatures. 400 | 401 | local e1 = Exchange.generate(exchange) 402 | local p1 = Exchange.export(e1) 403 | 404 | -- Gather corpus to sign. 405 | local selectionOut = 406 | table.concat { 407 | proposeOutBytes, 408 | proposeInBytes, 409 | p1 410 | } 411 | 412 | -- Encode and send Exchange packet 413 | local exchangeOut = { 414 | epubkey = p1, 415 | signature = key1:sign(selectionOut) 416 | } 417 | dump('exchangeOut', exchangeOut) 418 | Msg.writeFrame(stream, Protobuf.encodeTable(ExchangeSchema, exchangeOut)) 419 | 420 | -- Receive and decode their Exchange packet 421 | local exchangeIn = Protobuf.decodeTable(ExchangeSchema, Msg.readFrame(stream)) 422 | dump('exchangeIn', exchangeIn) 423 | 424 | ---------------------------------------------------------------------------- 425 | -- step 2.1. Verify -- verify their exchange packet is good. 426 | 427 | local e2 = Exchange.import(exchange, exchangeIn.epubkey) 428 | local selectionInBytes = 429 | table.concat { 430 | proposeInBytes, 431 | proposeOutBytes, 432 | exchangeIn.epubkey 433 | } 434 | 435 | local verified = key2:verify(selectionInBytes, exchangeIn.signature) 436 | dump('verified signature', verified) 437 | assert(verified, 'Signature verification failed in exchange') 438 | 439 | ---------------------------------------------------------------------------- 440 | -- step 2.2. Keys -- generate keys for mac + encryption 441 | 442 | local sharedSecret = Exchange.exchange(e1, e2) 443 | dump('sharedSecret', sharedSecret) 444 | 445 | local k1, k2 = keyStretcher(cipher, hash, sharedSecret) 446 | 447 | -- use random nonces to decide order. 448 | if order < 0 then 449 | k2, k1 = k1, k2 450 | end 451 | 452 | dump('k1', k1) 453 | dump('k2', k2) 454 | 455 | ---------------------------------------------------------------------------- 456 | -- step 2.3. MAC + Cipher -- prepare MAC + cipher 457 | 458 | stream = wrapStream(stream, cipher, hash, k1, k2) 459 | 460 | ---------------------------------------------------------------------------- 461 | -- step 3. Finish -- send expected message to verify encryption works (send local nonce) 462 | 463 | -- stream.writeChunk(proposeIn.rand) 464 | stream.writeChunk(proposeIn.rand) 465 | local confirm = stream.readChunk(16) 466 | dump('confirm', {confirm, proposeOut.rand}) 467 | assert(confirm == proposeOut.rand) 468 | 469 | -- TODO: exchange verification messages 470 | 471 | return stream 472 | end 473 | 474 | return Secio 475 | -------------------------------------------------------------------------------- /sha1.lua: -------------------------------------------------------------------------------- 1 | --[[lit-meta 2 | name = "creationix/sha1" 3 | version = "1.0.3" 4 | homepage = "https://github.com/luvit/lit/blob/master/deps/sha1.lua" 5 | description = "Pure Lua implementation of SHA1 using bitop" 6 | authors = { 7 | "Tim Caswell" 8 | } 9 | ]] 10 | 11 | -- http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf 12 | 13 | local bit = require('bit') 14 | local band = bit.band 15 | local bor = bit.bor 16 | local bxor = bit.bxor 17 | local lshift = bit.lshift 18 | local rshift = bit.rshift 19 | local rol = bit.rol 20 | local tobit = bit.tobit 21 | local tohex = bit.tohex 22 | 23 | local byte = string.byte 24 | local concat = table.concat 25 | local floor = math.floor 26 | 27 | local hasFFi, ffi = pcall(require, "ffi") 28 | local newBlock = hasFFi and function () 29 | return ffi.new("uint32_t[80]") 30 | end or function () 31 | local t = {} 32 | for i = 0, 79 do 33 | t[i] = 0 34 | end 35 | return t 36 | end 37 | 38 | local shared = newBlock() 39 | 40 | local function unsigned(n) 41 | return n < 0 and (n + 0x100000000) or n 42 | end 43 | 44 | local function create(sync) 45 | local h0 = 0x67452301 46 | local h1 = 0xEFCDAB89 47 | local h2 = 0x98BADCFE 48 | local h3 = 0x10325476 49 | local h4 = 0xC3D2E1F0 50 | -- The first 64 bytes (16 words) is the data chunk 51 | local W = sync and shared or newBlock() 52 | local offset = 0 53 | local shift = 24 54 | local totalLength = 0 55 | 56 | local update, write, processBlock, digest 57 | 58 | -- The user gave us more data. Store it! 59 | function update(chunk) 60 | local length = #chunk 61 | totalLength = totalLength + length * 8 62 | for i = 1, length do 63 | write(byte(chunk, i)) 64 | end 65 | end 66 | 67 | function write(data) 68 | W[offset] = bor(W[offset], lshift(band(data, 0xff), shift)) 69 | if shift > 0 then 70 | shift = shift - 8 71 | else 72 | offset = offset + 1 73 | shift = 24 74 | end 75 | if offset == 16 then 76 | return processBlock() 77 | end 78 | end 79 | 80 | -- No more data will come, pad the block, process and return the result. 81 | function digest() 82 | -- Pad 83 | write(0x80) 84 | if offset > 14 or (offset == 14 and shift < 24) then 85 | processBlock() 86 | end 87 | offset = 14 88 | shift = 24 89 | 90 | -- 64-bit length big-endian 91 | write(0x00) -- numbers this big aren't accurate in lua anyway 92 | write(0x00) -- ..So just hard-code to zero. 93 | write(totalLength > 0xffffffffff and floor(totalLength / 0x10000000000) or 0x00) 94 | write(totalLength > 0xffffffff and floor(totalLength / 0x100000000) or 0x00) 95 | for s = 24, 0, -8 do 96 | write(rshift(totalLength, s)) 97 | end 98 | 99 | -- At this point one last processBlock() should trigger and we can pull out the result. 100 | return concat { 101 | tohex(h0), 102 | tohex(h1), 103 | tohex(h2), 104 | tohex(h3), 105 | tohex(h4) 106 | } 107 | end 108 | 109 | -- We have a full block to process. Let's do it! 110 | function processBlock() 111 | 112 | -- Extend the sixteen 32-bit words into eighty 32-bit words: 113 | for i = 16, 79, 1 do 114 | W[i] = 115 | rol(bxor(W[i - 3], W[i - 8], W[i - 14], W[i - 16]), 1) 116 | end 117 | 118 | -- print("Block Contents:") 119 | -- for i = 0, 15 do 120 | -- print(string.format(" W[%d] = %s", i, tohex(W[i]))) 121 | -- end 122 | -- print() 123 | 124 | -- Initialize hash value for this chunk: 125 | local a = h0 126 | local b = h1 127 | local c = h2 128 | local d = h3 129 | local e = h4 130 | local f, k 131 | 132 | -- print(" A B C D E") 133 | -- local format = 134 | -- "t=%02d: %s %s %s %s %s" 135 | -- Main loop: 136 | for t = 0, 79 do 137 | if t < 20 then 138 | f = bxor(d, band(b, bxor(c, d))) 139 | k = 0x5A827999 140 | elseif t < 40 then 141 | f = bxor(b, c, d) 142 | k = 0x6ED9EBA1 143 | elseif t < 60 then 144 | f = bor(band(b, c), (band(d, bor(b, c)))) 145 | k = 0x8F1BBCDC 146 | else 147 | f = bxor(b, c, d) 148 | k = 0xCA62C1D6 149 | end 150 | e, d, c, b, a = 151 | d, 152 | c, 153 | rol(b, 30), 154 | a, 155 | tobit( 156 | unsigned(rol(a, 5)) + 157 | unsigned(f) + 158 | unsigned(e) + 159 | unsigned(k) + 160 | W[t] 161 | ) 162 | -- print(string.format(format, t, tohex(a), tohex(b), tohex(c), tohex(d), tohex(e))) 163 | end 164 | 165 | -- Add this chunk's hash to result so far: 166 | h0 = tobit(unsigned(h0) + a) 167 | h1 = tobit(unsigned(h1) + b) 168 | h2 = tobit(unsigned(h2) + c) 169 | h3 = tobit(unsigned(h3) + d) 170 | h4 = tobit(unsigned(h4) + e) 171 | 172 | -- The block is now reusable. 173 | offset = 0 174 | for i = 0, 15 do 175 | W[i] = 0 176 | end 177 | end 178 | 179 | return { 180 | update = update, 181 | digest = digest 182 | } 183 | 184 | end 185 | 186 | return function (buffer) 187 | -- Pass in false or nil to get a streaming interface. 188 | if not buffer then 189 | return create(false) 190 | end 191 | local shasum = create(true) 192 | shasum.update(buffer) 193 | return shasum.digest() 194 | end 195 | -------------------------------------------------------------------------------- /sha256.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local ffi = require 'ffi' 3 | local ror = bit.ror 4 | local bxor = bit.bxor 5 | local band = bit.band 6 | local bor = bit.bor 7 | local bnot = bit.bnot 8 | local tobit = bit.tobit 9 | local rshift = bit.rshift 10 | local lshift = bit.lshift 11 | local char = string.char 12 | local concat = table.concat 13 | 14 | local initial224 = ffi.new('uint32_t[8]', { 15 | 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, 16 | }) 17 | 18 | local initial256 = ffi.new('uint32_t[8]', { 19 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 20 | }) 21 | 22 | local k256 = ffi.new('uint32_t[64]', { 23 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 24 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 25 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 26 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 27 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 28 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 29 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 30 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 31 | }) 32 | 33 | ffi.cdef [[ 34 | struct state256 { 35 | uint32_t h[8]; 36 | uint32_t w[64]; 37 | intptr_t offset; 38 | size_t length; 39 | size_t digestLength; 40 | }; 41 | ]] 42 | 43 | local function uint32(num) 44 | return char( 45 | rshift(num, 24), 46 | band(rshift(num, 16), 0xff), 47 | band(rshift(num, 8), 0xff), 48 | band(num, 0xff) 49 | ) 50 | end 51 | 52 | local Sha256 = {} 53 | 54 | function Sha256:init() 55 | self.offset = 0 56 | self.length = 0 57 | self.digestLength = 32 58 | ffi.copy(self.h, initial256, 32) 59 | end 60 | 61 | function Sha256:init224() 62 | self.offset = 0 63 | self.length = 0 64 | self.digestLength = 28 65 | ffi.copy(self.h, initial224, 32) 66 | end 67 | 68 | function Sha256:update(message) 69 | local length = #message 70 | self.length = self.length + length 71 | local input = ffi.new("uint8_t[?]", length) 72 | ffi.copy(input, message, length) 73 | local offset = 0 74 | while offset < length do 75 | local needed = length - offset 76 | local available = tonumber(64 - self.offset) 77 | local slice = math.min(needed, available) 78 | for _ = 1, slice do 79 | local j = rshift(self.offset, 2) 80 | local mod = self.offset % 4 81 | if mod == 0 then self.w[j] = 0 end 82 | self.w[j] = bor(self.w[j], lshift(input[offset], (3 - mod) * 8)) 83 | offset = offset + 1 84 | self.offset = self.offset + 1 85 | end 86 | assert(self.offset <= 64) 87 | if self.offset == 64 then 88 | self:compress() 89 | end 90 | end 91 | end 92 | 93 | function Sha256:compress() 94 | -- p(ffi.string(self.w, 64)) 95 | -- Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: 96 | for i = 16, 63 do 97 | local s0 = bxor(ror(self.w[i - 15], 7), ror(self.w[i - 15], 18), rshift(self.w[i - 15], 3)) 98 | local s1 = bxor(ror(self.w[i - 2], 17), ror(self.w[i - 2], 19), rshift(self.w[i - 2], 10)) 99 | self.w[i] = self.w[i - 16] + s0 + self.w[i - 7] + s1 100 | end 101 | 102 | -- Initialize working variables to current hash value 103 | local a = self.h[0] 104 | local b = self.h[1] 105 | local c = self.h[2] 106 | local d = self.h[3] 107 | local e = self.h[4] 108 | local f = self.h[5] 109 | local g = self.h[6] 110 | local h = self.h[7] 111 | 112 | -- Compression function main loop 113 | for i = 0, 63 do 114 | local S1 = bxor(ror(e, 6), ror(e, 11), ror(e, 25)) 115 | local ch = bxor(band(e, f), band(bnot(e), g)) 116 | local temp1 = tobit(h + S1 + ch + k256[i] + self.w[i]) 117 | local S0 = bxor(ror(a, 2), ror(a, 13), ror(a, 22)) 118 | local maj = bxor(band(a, b), band(a, c), band(b, c)) 119 | local temp2 = tobit(S0 + maj) 120 | h = g 121 | g = f 122 | f = e 123 | e = tobit(d + temp1) 124 | d = c 125 | c = b 126 | b = a 127 | a = tobit(temp1 + temp2) 128 | end 129 | 130 | -- Add the compressed chunk to the current hash value: 131 | self.h[0] = self.h[0] + a 132 | self.h[1] = self.h[1] + b 133 | self.h[2] = self.h[2] + c 134 | self.h[3] = self.h[3] + d 135 | self.h[4] = self.h[4] + e 136 | self.h[5] = self.h[5] + f 137 | self.h[6] = self.h[6] + g 138 | self.h[7] = self.h[7] + h 139 | 140 | -- Reset write offset 141 | self.offset = 0 142 | end 143 | 144 | function Sha256:pad() 145 | local L = self.length * 8 146 | local K = tonumber((128 - 8 - 1 - self.offset) % 64) 147 | local high = tonumber(L / 0x100000000) 148 | local low = tonumber(L % 0x100000000) 149 | self:update('\128' .. ('\000'):rep(K) .. uint32(high) .. uint32(low)) 150 | end 151 | 152 | function Sha256:digest() 153 | self:pad() 154 | assert(self.offset == 0) 155 | local parts = {} 156 | for i = 0, tonumber(self.digestLength) / 4 - 1 do 157 | parts[i + 1] = uint32(self.h[i]) 158 | end 159 | return concat(parts) 160 | end 161 | 162 | local create = ffi.metatype('struct state256', {__index = Sha256}) 163 | 164 | return { 165 | [256] = function (message) 166 | local shasum = create() 167 | shasum:init() 168 | -- Pass in false or nil to get a streaming interface. 169 | if not message then 170 | return shasum 171 | end 172 | shasum:update(message) 173 | return shasum:digest() 174 | end, 175 | [224] = function (message) 176 | local shasum = create() 177 | shasum:init224() 178 | -- Pass in false or nil to get a streaming interface. 179 | if not message then 180 | return shasum 181 | end 182 | shasum:update(message) 183 | return shasum:digest() 184 | end, 185 | } -------------------------------------------------------------------------------- /sha512.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local ffi = require 'ffi' 3 | local ror = bit.ror 4 | local bxor = bit.bxor 5 | local band = bit.band 6 | local bor = bit.bor 7 | local bnot = bit.bnot 8 | local rshift = bit.rshift 9 | local lshift = bit.lshift 10 | local char = string.char 11 | local concat = table.concat 12 | 13 | local initial384 = ffi.new('uint64_t[8]', { 14 | 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, 15 | 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL, 16 | }) 17 | 18 | local initial512 = ffi.new('uint64_t[8]', { 19 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 20 | 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL, 21 | }) 22 | 23 | local k = ffi.new('uint64_t[80]', { 24 | 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 25 | 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 26 | 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 27 | 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 28 | 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, 29 | 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 30 | 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 31 | 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 32 | 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 33 | 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 34 | 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 35 | 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 36 | 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 37 | 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 38 | 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 39 | 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, 40 | }) 41 | 42 | ffi.cdef[[ 43 | struct state512 { 44 | uint64_t h[8]; 45 | uint64_t w[80]; 46 | intptr_t offset; 47 | size_t length; 48 | size_t digestLength; 49 | }; 50 | ]] 51 | 52 | local function uint32(num) 53 | return char( 54 | rshift(num, 24), 55 | band(rshift(num, 16), 0xff), 56 | band(rshift(num, 8), 0xff), 57 | band(num, 0xff) 58 | ) 59 | end 60 | 61 | local Sha512 = {} 62 | 63 | function Sha512:init() 64 | self.offset = 0 65 | self.length = 0 66 | self.digestLength = 64 67 | ffi.copy(self.h, initial512, 64) 68 | end 69 | 70 | function Sha512:init384() 71 | self.offset = 0 72 | self.length = 0 73 | self.digestLength = 48 74 | ffi.copy(self.h, initial384, 64) 75 | end 76 | 77 | function Sha512:update(message) 78 | local length = #message 79 | self.length = self.length + length 80 | local input = ffi.new("uint8_t[?]", length) 81 | ffi.copy(input, message, length) 82 | local offset = 0 83 | while offset < length do 84 | local needed = length - offset 85 | local available = tonumber(128 - self.offset) 86 | local slice = math.min(needed, available) 87 | for _ = 1, slice do 88 | local j = rshift(self.offset, 3) 89 | local mod = self.offset % 8 90 | if mod == 0 then self.w[j] = 0 end 91 | self.w[j] = bor(self.w[j], lshift(1ULL * input[offset], (7 - mod) * 8)) 92 | offset = offset + 1 93 | self.offset = self.offset + 1 94 | end 95 | assert(self.offset <= 128) 96 | if self.offset == 128 then 97 | self:compress() 98 | end 99 | end 100 | end 101 | 102 | function Sha512:compress() 103 | -- p(ffi.string(self.w, 128)) 104 | -- Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: 105 | for i = 16, 79 do 106 | local s0 = bxor(ror(self.w[i - 15], 1), ror(self.w[i - 15], 8), rshift(self.w[i - 15], 7)) 107 | local s1 = bxor(ror(self.w[i - 2], 19), ror(self.w[i - 2], 61), rshift(self.w[i - 2], 6)) 108 | self.w[i] = self.w[i - 16] + s0 + self.w[i - 7] + s1 109 | end 110 | 111 | -- Initialize working variables to current hash value 112 | local a = self.h[0] 113 | local b = self.h[1] 114 | local c = self.h[2] 115 | local d = self.h[3] 116 | local e = self.h[4] 117 | local f = self.h[5] 118 | local g = self.h[6] 119 | local h = self.h[7] 120 | 121 | -- Compression function main loop 122 | for i = 0, 79 do 123 | local S1 = bxor(ror(e, 14), ror(e, 18), ror(e, 41)) 124 | local ch = bxor(band(e, f), band(bnot(e), g)) 125 | local temp1 = h + S1 + ch + k[i] + self.w[i] 126 | local S0 = bxor(ror(a, 28), ror(a, 34), ror(a, 39)) 127 | local maj = bxor(band(a, b), band(a, c), band(b, c)) 128 | local temp2 = S0 + maj 129 | h = g 130 | g = f 131 | f = e 132 | e = d + temp1 133 | d = c 134 | c = b 135 | b = a 136 | a = temp1 + temp2 137 | end 138 | 139 | -- Add the compressed chunk to the current hash value: 140 | self.h[0] = self.h[0] + a 141 | self.h[1] = self.h[1] + b 142 | self.h[2] = self.h[2] + c 143 | self.h[3] = self.h[3] + d 144 | self.h[4] = self.h[4] + e 145 | self.h[5] = self.h[5] + f 146 | self.h[6] = self.h[6] + g 147 | self.h[7] = self.h[7] + h 148 | 149 | -- Reset write offset 150 | self.offset = 0 151 | end 152 | 153 | function Sha512:pad() 154 | local L = self.length * 8 155 | local K = tonumber((256 - 16 - 1 - self.offset) % 128) + 8 156 | local high = tonumber(L / 0x100000000) 157 | local low = tonumber(L % 0x100000000) 158 | self:update('\128' .. ('\000'):rep(K) .. uint32(high) .. uint32(low)) 159 | end 160 | 161 | function Sha512:digest() 162 | self:pad() 163 | assert(self.offset == 0) 164 | local parts = {} 165 | for i = 1, tonumber(self.digestLength) / 8 do 166 | local h = self.h[i - 1] 167 | local high = tonumber(h / 0x100000000) 168 | local low = tonumber(h % 0x100000000) 169 | parts[i] = uint32(high) .. uint32(low) 170 | end 171 | return concat(parts) 172 | end 173 | 174 | local create = ffi.metatype('struct state512', {__index = Sha512}) 175 | 176 | return { 177 | [512] = function (message) 178 | local shasum = create() 179 | shasum:init() 180 | if not message then return shasum end 181 | shasum:update(message) 182 | return shasum:digest() 183 | end, 184 | [384] = function (message) 185 | local shasum = create() 186 | shasum:init384() 187 | if not message then return shasum end 188 | shasum:update(message) 189 | return shasum:digest() 190 | end, 191 | } -------------------------------------------------------------------------------- /stream-wrap.lua: -------------------------------------------------------------------------------- 1 | local makeCallback = require('make-callback') 2 | local Connection = require 'connection' 3 | 4 | -- This is a simple wrapper around raw libuv streams that lets 5 | -- us have pull-style streams with a nice coroutine based interface. 6 | -- Read calls will block till there is data. 7 | -- Write calls will block will the buffer is no longer full (applying backpressure). 8 | -- The read calls will automatically pause and resume the read stream to apply 9 | -- backpressure to the remote writer as well. 10 | return function(socket, onError) 11 | local paused = true 12 | local stream = Connection.newPush() 13 | 14 | function stream.onStart() 15 | if not paused then 16 | return 17 | end 18 | paused = false 19 | local function onRead(error, value) 20 | p('in', value) 21 | if error and onError then 22 | onError(error) 23 | end 24 | return stream.onChunk(value) 25 | end 26 | socket:read_start(onRead) 27 | end 28 | 29 | function stream.onStop() 30 | if paused then 31 | return 32 | end 33 | paused = true 34 | socket:read_stop() 35 | end 36 | 37 | function stream.writeChunk(value) 38 | p('out', value) 39 | socket:write(value, makeCallback()) 40 | coroutine.yield() 41 | end 42 | 43 | stream.socket = socket 44 | return stream 45 | end -------------------------------------------------------------------------------- /switch.lua: -------------------------------------------------------------------------------- 1 | local connect = require 'uv-socket' 2 | local wrapStream = require 'stream-wrap' 3 | local Mplex = require 'mplex' 4 | local Multiselect = require 'multiselect' 5 | local Secio = require 'secio' 6 | 7 | local Switch = {} 8 | 9 | function Switch.dial(host, port) 10 | -- Derive read/write functions that work with framed messages 11 | local stream = wrapStream(assert(connect {host = host, port = port})) 12 | 13 | -- Negotiate protocol 14 | -- Start test server with `ipfs daemon` 15 | Multiselect.negotiate(stream, '/secio/1.0.0') 16 | stream = Secio.wrap(stream) 17 | 18 | -- Upgrade to mplex protocol and return handle to mp object. 19 | return Mplex.start(stream) 20 | end 21 | 22 | return Switch 23 | -------------------------------------------------------------------------------- /test-base-64.lua: -------------------------------------------------------------------------------- 1 | local base64 = require 'base-64'( 2 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' 3 | ) 4 | 5 | assert(base64.encode("") == "") 6 | assert(base64.encode("f") == "Zg==") 7 | assert(base64.encode("fo") == "Zm8=") 8 | assert(base64.encode("foo") == "Zm9v") 9 | assert(base64.encode("foob") == "Zm9vYg==") 10 | assert(base64.encode("fooba") == "Zm9vYmE=") 11 | assert(base64.encode("foobar") == "Zm9vYmFy") 12 | 13 | assert(base64.decode("") == "") 14 | assert(base64.decode("Zg==") == "f") 15 | assert(base64.decode("Zm8=") == "fo") 16 | assert(base64.decode("Zm9v") == "foo") 17 | assert(base64.decode("Zm9vYg==") == "foob") 18 | assert(base64.decode("Zm9vYmE=") == "fooba") 19 | assert(base64.decode("Zm9vYmFy") == "foobar") 20 | -------------------------------------------------------------------------------- /test-base-x.lua: -------------------------------------------------------------------------------- 1 | local baseX = require './base-x' 2 | 3 | local b2 = baseX "01" 4 | local b8 = baseX "01234567" 5 | local b11 = baseX "0123456789a" 6 | local b16 = baseX "0123456789abcdef" 7 | local b32 = baseX "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 8 | local zb32 = baseX "ybndrfg8ejkmcpqxot1uwisza345h769" 9 | local b36 = baseX "0123456789abcdefghijklmnopqrstuvwxyz" 10 | local b58 = baseX "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 11 | local b62 = baseX "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 12 | local b64 = baseX "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 13 | local b66 = baseX "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~" 14 | local b94 = baseX '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' 15 | 16 | local function test(name, base) 17 | print("Testing " .. name) 18 | local msg = "Hello world, let's compute some bases" 19 | local encoded = base.encode(msg) 20 | print(encoded) 21 | local decoded = base.decode(encoded) 22 | assert(msg == decoded, "roundtrip failed") 23 | end 24 | 25 | test('b2', b2) 26 | test('b8', b8) 27 | test('b11', b11) 28 | test('b16', b16) 29 | test('b32', b32) 30 | test('zb32', zb32) 31 | test('b36', b36) 32 | test('b58', b58) 33 | test('b62', b62) 34 | test('b64', b64) 35 | test('b66', b66) 36 | test('b94', b94) 37 | -------------------------------------------------------------------------------- /test-cid.lua: -------------------------------------------------------------------------------- 1 | local p = require'pretty-print'.prettyPrint 2 | local Cid = require 'cid' 3 | require 'cid-cbor' 4 | local Cbor = require 'cbor' 5 | 6 | p(Cid) 7 | p(Cid.decode('zb2rhe5P4gXftAwvA4eXQ5HJwsER2owDyS9sKaQRRVQPn93bA')) 8 | p(Cid.decode('QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB')) 9 | p(Cid.link0('Hello World\n')) 10 | p(Cid.link0('')) 11 | p(Cid.decode(Cid.encode(Cid.link0('')))) 12 | p(Cid.link('Hello World\n')) 13 | p(Cid.link('')) 14 | p(Cid.decode(Cid.encode(Cid.link('')))) 15 | p(Cid.link('{}', {multihash = 'sha2-256', multicodec = 'dag-json', multibase = 'z'})) 16 | 17 | local link = Cid.link('', {multihash='blake2b-256'}) 18 | p(link) 19 | local serialized = Cbor.encode(link) 20 | p(serialized) 21 | local deserialized = Cbor.decode(serialized) 22 | p(deserialized) -------------------------------------------------------------------------------- /test-filecoin.lua: -------------------------------------------------------------------------------- 1 | local p = require "pretty-print".prettyPrint 2 | local hex = require 'hex' 3 | local filecoin = require 'filecoin' 4 | local Address = filecoin.Address 5 | local Block = filecoin.Block 6 | local Message = filecoin.Message 7 | local SignedMessage = filecoin.SignedMessage 8 | local Cbor = require 'cbor' 9 | local bin = Cbor.bin 10 | 11 | Address.network = 't' 12 | 13 | local address = Address.new() 14 | address.protocol = 0 15 | address.payload = 1234 16 | p(address) 17 | print(address) 18 | p(Cbor.encode(address)) 19 | p(Cbor.decode(Cbor.encode(address))) 20 | p(Address.new(Cbor.decode(Cbor.encode(address)))) 21 | -- local block = Block.new() 22 | -- p(block) 23 | -- p(Cbor.encode(block)) 24 | -- p(Cbor.decode(Cbor.encode(block))) 25 | -- local message = Message.new() 26 | -- p(message) 27 | -- local signedMessage = SignedMessage.new() 28 | -- p(signedMessage) 29 | 30 | -- local messagebin = hex.decode "d82c865501fd1d0f4dfcd7e99afcb99a8326b7dc459d32c6285501b882619d46558f3d9e316d11b48dcf211327026a1875c245037e11d600666d6574686f644d706172616d73617265676f6f64" 31 | -- p(messagebin) 32 | -- -- local message = cbor.decode(messagebin) 33 | -- -- p(message) 34 | -- -- p(messagebin) 35 | -- -- p(cbor.encode(message)) 36 | 37 | -- -- local block1 = hex.decode "d82b895501fd1d0f4dfcd7e99afcb99a8326b7dc459d32c628814a69616d617469636b6574566920616d20616e20656c656374696f6e2070726f6f6681d82a5827000171a0e40220ce25e43084e66e5a92f8c3066c00c0eb540ac2f2a173326507908da06b96f678c242bb6a1a0012d687d82a5827000171a0e40220ce25e43084e66e5a92f8c3066c00c0eb540ac2f2a173326507908da06b96f6788080" 38 | -- -- local block = cbor.decode(block1) 39 | -- -- local encoded = cbor.encode(block) 40 | -- -- p(block) 41 | -- -- p(cbor.decode(encoded)) 42 | -- -- p(block1) 43 | -- -- p(encoded) 44 | -------------------------------------------------------------------------------- /test-gimli.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | local gimli = require 'gimli-permutation' 3 | 4 | local function dump(state) 5 | print(string.format('%08x %08x %08x %08x %08x %08x', state[0], state[1], state[2], state[3], state[4], state[5])) 6 | print(string.format('%08x %08x %08x %08x %08x %08x', state[6], state[7], state[8], state[9], state[10], state[11])) 7 | end 8 | 9 | local state = ffi.new 'uint32_t[3*4]' 10 | dump(state) 11 | gimli(state) 12 | print() 13 | dump(state) 14 | -------------------------------------------------------------------------------- /test-multibase.lua: -------------------------------------------------------------------------------- 1 | local p = require'pretty-print'.prettyPrint 2 | local Multibase = require 'multibase' 3 | 4 | local messages = { 5 | "Decentralize everything!!", 6 | { 7 | "0010001000110010101100011011001010110111001110100011100100110000101101100011010010111101001100101001" .. 8 | "00000011001010111011001100101011100100111100101110100011010000110100101101110011001110010000100100001", 9 | "72106254331267164344605543227514510062566312711713506415133463441102", 10 | "9429328951066508984658627669258025763026247056774804621697313", 11 | "f446563656e7472616c697a652065766572797468696e672121", 12 | "F446563656E7472616C697A652065766572797468696E672121", 13 | "birswgzloorzgc3djpjssazlwmvzhs5dinfxgoijb", 14 | "BIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJB", 15 | "v8him6pbeehp62r39f9ii0pbmclp7it38d5n6e891", 16 | "V8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E891", 17 | "cirswgzloorzgc3djpjssazlwmvzhs5dinfxgoijb", 18 | "CIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJB", 19 | "t8him6pbeehp62r39f9ii0pbmclp7it38d5n6e891", 20 | "T8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E891", 21 | "het1sg3mqqt3gn5djxj11y3msci3817depfzgqejb", 22 | "Ztwe7gVTeK8wswS1gf8hrgAua9fcw9reboD", 23 | "zUXE7GvtEk8XTXs1GF8HSGbVA9FCX9SEBPe", 24 | "mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ", 25 | "MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ==", 26 | "uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ", 27 | "URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ==", 28 | }, 29 | "yes mani !", 30 | { 31 | "001111001011001010111001100100000011011010110000101101110011010010010000000100001", 32 | "7362625631006654133464440102", 33 | "9573277761329450583662625", 34 | "f796573206d616e692021", 35 | "F796573206D616E692021", 36 | "bpfsxgidnmfxgsibb", 37 | "BPFSXGIDNMFXGSIBB", 38 | "vf5in683dc5n6i811", 39 | "VF5IN683DC5N6I811", 40 | "cpfsxgidnmfxgsibb", 41 | "CPFSXGIDNMFXGSIBB", 42 | "tf5in683dc5n6i811", 43 | "TF5IN683DC5N6I811", 44 | "hxf1zgedpcfzg1ebb", 45 | "Z7Pznk19XTTzBtx", 46 | "z7paNL19xttacUY", 47 | "meWVzIG1hbmkgIQ", 48 | "MeWVzIG1hbmkgIQ==", 49 | "ueWVzIG1hbmkgIQ", 50 | "UeWVzIG1hbmkgIQ==", 51 | }, 52 | "hello world", 53 | { 54 | "00110100001100101011011000110110001101111001000000111011101101111011100100110110001100100", 55 | "7320625543306744035667562330620", 56 | "9126207244316550804821666916", 57 | "f68656c6c6f20776f726c64", 58 | "F68656C6C6F20776F726C64", 59 | "bnbswy3dpeb3w64tmmq", 60 | "BNBSWY3DPEB3W64TMMQ", 61 | "vd1imor3f41rmusjccg", 62 | "VD1IMOR3F41RMUSJCCG", 63 | "cnbswy3dpeb3w64tmmq======", 64 | "CNBSWY3DPEB3W64TMMQ======", 65 | "td1imor3f41rmusjccg======", 66 | "TD1IMOR3F41RMUSJCCG======", 67 | "hpb1sa5dxrb5s6hucco", 68 | "ZrTu1dk6cWsRYjYu", 69 | "zStV1DL6CwTryKyV", 70 | "maGVsbG8gd29ybGQ", 71 | "MaGVsbG8gd29ybGQ=", 72 | "uaGVsbG8gd29ybGQ", 73 | "UaGVsbG8gd29ybGQ=", 74 | }, 75 | "\x00yes mani !", 76 | { 77 | "00000000001111001011001010111001100100000011011010110000101101110011010010010000000100001", 78 | "7000745453462015530267151100204", 79 | "90573277761329450583662625", 80 | "f00796573206d616e692021", 81 | "F00796573206D616E692021", 82 | "bab4wk4zanvqw42jaee", 83 | "BAB4WK4ZANVQW42JAEE", 84 | "v01smasp0dlgmsq9044", 85 | "V01SMASP0DLGMSQ9044", 86 | "cab4wk4zanvqw42jaee======", 87 | "CAB4WK4ZANVQW42JAEE======", 88 | "t01smasp0dlgmsq9044======", 89 | "T01SMASP0DLGMSQ9044======", 90 | "hybhskh3ypiosh4jyrr", 91 | "Z17Pznk19XTTzBtx", 92 | "z17paNL19xttacUY", 93 | "mAHllcyBtYW5pICE", 94 | "MAHllcyBtYW5pICE=", 95 | "uAHllcyBtYW5pICE", 96 | "UAHllcyBtYW5pICE=", 97 | }, 98 | "\x00\x00yes mani !", 99 | { 100 | "0000000000000000001111001011001010111001100100000011011010110000101101110011010010010000000100001", 101 | "700000171312714403326055632220041", 102 | "900573277761329450583662625", 103 | "f0000796573206d616e692021", 104 | "F0000796573206D616E692021", 105 | "baaahszltebwwc3tjeaqq", 106 | "BAAAHSZLTEBWWC3TJEAQQ", 107 | "v0007ipbj41mm2rj940gg", 108 | "V0007IPBJ41MM2RJ940GG", 109 | "caaahszltebwwc3tjeaqq====", 110 | "CAAAHSZLTEBWWC3TJEAQQ====", 111 | "t0007ipbj41mm2rj940gg====", 112 | "T0007IPBJ41MM2RJ940GG====", 113 | "hyyy813murbssn5ujryoo", 114 | "Z117Pznk19XTTzBtx", 115 | "z117paNL19xttacUY", 116 | "mAAB5ZXMgbWFuaSAh", 117 | "MAAB5ZXMgbWFuaSAh", 118 | "uAAB5ZXMgbWFuaSAh", 119 | "UAAB5ZXMgbWFuaSAh", 120 | }, 121 | } 122 | 123 | collectgarbage() 124 | local message 125 | for _, list in ipairs(messages) do 126 | collectgarbage() 127 | if type(list) == 'string' then 128 | message = list 129 | print("\nMessage:") 130 | p(message) 131 | print() 132 | else 133 | collectgarbage() 134 | for _, input in ipairs(list) do 135 | collectgarbage() 136 | local code = input:sub(1,1) 137 | collectgarbage() 138 | print("Encoding " .. code) 139 | collectgarbage() 140 | local encoded, name = Multibase.encode(message, code) 141 | collectgarbage() 142 | p(name, encoded) 143 | collectgarbage() 144 | if encoded ~= input then 145 | print("Expected: " .. input) 146 | print("But got: " .. encoded) 147 | assert(encoded == input) 148 | end 149 | collectgarbage() 150 | print("Decoding " .. code) 151 | local decoded, name2 = Multibase.decode(encoded) 152 | p(name2, decoded) 153 | collectgarbage() 154 | if decoded ~= message then 155 | print("Expected: " .. message) 156 | print("But got: " .. decoded) 157 | assert(decoded == message) 158 | end 159 | collectgarbage() 160 | assert(name == name2) 161 | collectgarbage() 162 | end 163 | collectgarbage() 164 | end 165 | end 166 | collectgarbage() 167 | -------------------------------------------------------------------------------- /test-multihash.lua: -------------------------------------------------------------------------------- 1 | local rep = string.rep 2 | local p = require 'pretty-print'.prettyPrint 3 | local Multihash = require "multihash" 4 | local hex = require 'hex' 5 | 6 | local tests = { 7 | { input = '', name = 'sha1', 8 | multi = '1114da39a3ee5e6b4b0d3255bfef95601890afd80709', 9 | hash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' }, 10 | { input = '', name = 'sha1', givenLength = 10, 11 | multi = '110ada39a3ee5e6b4b0d3255', 12 | hash = 'da39a3ee5e6b4b0d3255' }, 13 | { input = rep('multihash', 100), name = 'sha1', 14 | multi = '1114c939afc3963385d36fabda338baeebe19a0edb58', 15 | hash = 'c939afc3963385d36fabda338baeebe19a0edb58' }, 16 | { input = '', name = 'sha2-256', 17 | multi = '1220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 18 | hash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }, 19 | { input = rep('multihash', 100), name = 'sha2-256', 20 | multi = '1220cc2c01cd11317f29716c27679166338d4b5eeb0de5f3692a29e24d36498abd28', 21 | hash = 'cc2c01cd11317f29716c27679166338d4b5eeb0de5f3692a29e24d36498abd28' }, 22 | { input = rep('multihash', 100), name = 'sha2-256', givenLength = 20, 23 | multi = '1214cc2c01cd11317f29716c27679166338d4b5eeb0d', 24 | hash = 'cc2c01cd11317f29716c27679166338d4b5eeb0d' }, 25 | { input = '', name = 'sha2-512', 26 | multi = '1340cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', 27 | hash = 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' }, 28 | { input = rep('multihash', 100), name = 'sha2-512', 29 | multi = '134051566967f6aaca30b43cd7528d73f5aa9c14965a5b68da695fdfa783fcd0c22280e3f858b9ea6333b29dc9d118e750902cee5055eb040b26bd8aecbfe7be412c', 30 | hash = '51566967f6aaca30b43cd7528d73f5aa9c14965a5b68da695fdfa783fcd0c22280e3f858b9ea6333b29dc9d118e750902cee5055eb040b26bd8aecbfe7be412c' }, 31 | { input = rep('multihash', 100), name = 'sha2-512', givenLength = 40, 32 | multi = '132851566967f6aaca30b43cd7528d73f5aa9c14965a5b68da695fdfa783fcd0c22280e3f858b9ea6333', 33 | hash = '51566967f6aaca30b43cd7528d73f5aa9c14965a5b68da695fdfa783fcd0c22280e3f858b9ea6333' }, 34 | { input = '', name = 'blake2b-256', 35 | multi = 'a0e402200e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8', 36 | hash = '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' }, 37 | { input = rep('multihash', 100), name = 'blake2b-256', 38 | multi = 'a0e402204556cf646984479710c85a8109001a89330c775e7420bea37fd4f6ddd2c35121', 39 | hash = '4556cf646984479710c85a8109001a89330c775e7420bea37fd4f6ddd2c35121' }, 40 | { input = rep('multihash', 100), name = 'blake2b-256', givenLength = 20, 41 | multi = 'a0e402144556cf646984479710c85a8109001a89330c775e', 42 | hash = '4556cf646984479710c85a8109001a89330c775e' }, 43 | { input = '', name = 'blake2b-384', 44 | multi = 'b0e40230b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100', 45 | hash = 'b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100' }, 46 | { input = '', name = 'blake2b-224', 47 | multi = '9ce4021c836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07', 48 | hash = '836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07' }, 49 | { input = '', name = 'blake2b-160', 50 | multi = '94e402143345524abf6bbe1809449224b5972c41790b6cf2', 51 | hash = '3345524abf6bbe1809449224b5972c41790b6cf2' }, 52 | { input = '', name = 'blake2s-160', 53 | multi = 'd4e40214354c9c33f735962418bdacb9479873429c34916f', 54 | hash = '354c9c33f735962418bdacb9479873429c34916f' }, 55 | { input = '', name = 'blake2s-224', 56 | multi = 'dce4021c1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4', 57 | hash = '1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4' }, 58 | } 59 | 60 | for _, test in ipairs(tests) do 61 | print() 62 | p(test) 63 | local encoded, decoded, name, multi, hash 64 | encoded, name = Multihash.hash(test.input, test.name, test.givenLength) 65 | multi = hex.encode(encoded) 66 | p(multi) 67 | assert(multi == test.multi, 'multihash encoding failure') 68 | assert(name == test.name, 'encoding name mismatch') 69 | decoded, name = Multihash.decode(encoded) 70 | hash = hex.encode(decoded) 71 | p(name, hash) 72 | assert(hash == test.hash, 'decoding hash mismatch') 73 | assert(name == test.name, 'decoding name mistmatch') 74 | assert(Multihash.verify(test.input, encoded), "Verification failed") 75 | assert(not Multihash.verify(test.input .. 'x', encoded), "Inverse verification failed") 76 | end 77 | -------------------------------------------------------------------------------- /test-sha2.lua: -------------------------------------------------------------------------------- 1 | local p = require 'pretty-print'.prettyPrint 2 | local sha256 = require('sha256')[256] 3 | local sha224 = require('sha256')[224] 4 | local sha384 = require('sha512')[384] 5 | local sha512 = require('sha512')[512] 6 | local hex = require 'hex' 7 | 8 | -- local h = sha256() 9 | -- h:init() 10 | -- h:update(('Hello'):rep(11)) 11 | -- h:update(('World'):rep(20)) 12 | -- for i = 1, 10 do 13 | -- h:update('More bits ' .. i) 14 | -- end 15 | -- p(tohex(h:digest())) 16 | local tests = { 17 | { '', 18 | 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', 19 | 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 20 | '38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da' .. 21 | '274edebfe76f65fbd51ad2f14898b95b', 22 | 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce' .. 23 | '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' }, 24 | { 'Hello World', 25 | 'c4890faffdb0105d991a461e668e276685401b02eab1ef4372795047', 26 | 'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e', 27 | '99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc8' .. 28 | '03d0e93863a7c3d90f86beee782f4f3f', 29 | '2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f2' .. 30 | '7e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b' }, 31 | { 'Decentralize the Web for the Better of Humanity!', 32 | '6ee0160e8dcb0ef4dfe281694a9d2e21ca8fbac4932821523dce494a', 33 | '92f45f9b6e0143c200759199dd75c23efc3903338ce8e62a9b2786f6fa2cec70', 34 | '3a355ae3e484c49ed1ee4c3d36cb24eee1c53596972d6c427f6be2ee31abd74b' .. 35 | '37f1ef155c98ead957b9a3cd4b9a37c9', 36 | '0a23b4fafad705dd1e171c0dd9678ac3799c4e0707df0a4b5bfe4dcea319c7d9' .. 37 | 'e2c2047d69b82a9b64e38c79b01c7b8e81354a55dacd124506d052df958036e8' }, 38 | { '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', 39 | '43f95590b27f2afde6dd97d951f5ba4fe1d154056ec3f8ffeaea6347', 40 | 'a8ae6e6ee929abea3afcfc5258c8ccd6f85273e0d4626d26c7279f3250f77c8e', 41 | '648a627ba7edae512ab128eb8e4ad9cc13c9e89da332f71fe767f1c4dd0e5c2b' .. 42 | 'd3f83009b2855c02c7c7e488bcfc84dc', 43 | 'ad2981aa58beca63a49b8831274b89d81766a23d7932474f03e55cf00cbe2700' .. 44 | '4e66fd0912aed0b3cb1afee2aa904115c89db49d6c9bad785523023a9c309561' }, 45 | { '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' .. 46 | '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', 47 | '5c515ae64e8b4b4ec314fc4160f11945b3e6850f1d10c9390026c772', 48 | 'b320e85978db05134003a2914eebddd8d3b8726818f2e2c679e1898c721562a9', 49 | 'f932b89b678dbdddb555807703b3e4ff99d7082cc4008d3a623f40361caa24f8' .. 50 | 'b53f7b112ed46f027ff66ef842d2d08c', 51 | '451e75996b8939bc540be780b33d2e5ab20d6e2a2b89442c9bfe6b4797f6440d' .. 52 | 'ac65c58b6aff10a2ca34c37735008d671037fa4081bf56b4ee243729fa5e768e'} 53 | } 54 | for i = 1, #tests do 55 | local message, expected224, expected256, expected384, expected512 = unpack(tests[i]) 56 | collectgarbage() 57 | collectgarbage() 58 | print() 59 | p { message = message } 60 | print 'SHA-224' 61 | p { expect = expected224 } 62 | local actual = sha224(message) 63 | collectgarbage() 64 | collectgarbage() 65 | p { actual = hex.encode(actual) } 66 | assert(hex.decode(expected224) == actual, 'sha-224 hash mismatch') 67 | print 'SHA-256' 68 | p { expect = expected256 } 69 | actual = sha256(message) 70 | collectgarbage() 71 | collectgarbage() 72 | p { actual = hex.encode(actual) } 73 | assert(hex.decode(expected256) == actual, 'sha-256 hash mismatch') 74 | print 'SHA-384' 75 | p { expect = expected384 } 76 | actual = sha384(message) 77 | collectgarbage() 78 | collectgarbage() 79 | p { actual = hex.encode(actual) } 80 | assert(hex.decode(expected384) == actual, 'sha-384 hash mismatch') 81 | print 'SHA-512' 82 | p { expect = expected512 } 83 | actual = sha512(message) 84 | collectgarbage() 85 | collectgarbage() 86 | p { actual = hex.encode(actual) } 87 | assert(hex.decode(expected512) == actual, 'sha-512 hash mismatch') 88 | end 89 | -------------------------------------------------------------------------------- /tests/multibase1.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/lua-filecoin/6a2e3b40ba2f978bf50057ac388d9a4a907146d9/tests/multibase1.csv -------------------------------------------------------------------------------- /tests/multibase2.csv: -------------------------------------------------------------------------------- 1 | encoding, "yes mani !" 2 | base2, "001111001011001010111001100100000011011010110000101101110011010010010000000100001" 3 | base8, "7171312714403326055632220041" 4 | base10, "9573277761329450583662625" 5 | base16, "f796573206d616e692021" 6 | base16upper, "F796573206D616E692021" 7 | base32, "bpfsxgidnmfxgsibb" 8 | base32upper, "BPFSXGIDNMFXGSIBB" 9 | base32hex, "vf5in683dc5n6i811" 10 | base32hexupper, "VF5IN683DC5N6I811" 11 | base32pad, "cpfsxgidnmfxgsibb" 12 | base32padupper, "CPFSXGIDNMFXGSIBB" 13 | base32hexpad, "tf5in683dc5n6i811" 14 | base32hexpadupper, "TF5IN683DC5N6I811" 15 | base32z, "hxf1zgedpcfzg1ebb" 16 | base58flickr, "Z7Pznk19XTTzBtx" 17 | base58btc, "z7paNL19xttacUY" 18 | base64, "meWVzIG1hbmkgIQ" 19 | base64pad, "MeWVzIG1hbmkgIQ==" 20 | base64url, "ueWVzIG1hbmkgIQ" 21 | base64urlpad, "UeWVzIG1hbmkgIQ==" 22 | -------------------------------------------------------------------------------- /tests/test-exchange.lua: -------------------------------------------------------------------------------- 1 | dofile 'pretty-print.lua' 2 | local Exchange = require 'exchange' 3 | local generate = Exchange.generate 4 | local import = Exchange.import 5 | local export = Exchange.export 6 | local free = Exchange.free 7 | local exchange = Exchange.exchange 8 | 9 | local function test(curve) 10 | local priv1 = generate(curve) 11 | local priv2 = generate(curve) 12 | local pub1 = import(curve, export(priv1)) 13 | local pub2 = import(curve, export(priv2)) 14 | p(priv1) 15 | p(pub2) 16 | local secret1 = exchange(priv1, pub2) 17 | p(secret1) 18 | p(priv2) 19 | p(pub1) 20 | local secret2 = exchange(priv2, pub1) 21 | p(secret2) 22 | assert(secret1 == secret2) 23 | free(priv1) 24 | free(priv2) 25 | free(pub1) 26 | free(pub2) 27 | end 28 | 29 | test('P-256') 30 | test('P-384') 31 | test('P-521') 32 | 33 | Exchange.cleanup() -------------------------------------------------------------------------------- /tests/test-stream-reader.lua: -------------------------------------------------------------------------------- 1 | local defer = require 'defer' 2 | local makeReader = require 'stream-reader' 3 | local Utils = require './utils' 4 | local mockStream = Utils.mockStream 5 | local deadbeef = Utils.deadbeef 6 | 7 | local function pause() 8 | local thread = coroutine.running() 9 | defer( 10 | function() 11 | assert(coroutine.resume(thread)) 12 | end 13 | ) 14 | coroutine.yield() 15 | end 16 | 17 | local function test(index) 18 | local stream = mockStream() 19 | local read = makeReader(stream) 20 | local random = deadbeef(index * 13) 21 | 22 | coroutine.wrap( 23 | function() 24 | for i = 1, 100 do 25 | if random() % 2 == 1 then 26 | pause() 27 | end 28 | stream.push('item-' .. i) 29 | end 30 | if random() % 2 == 1 then 31 | pause() 32 | end 33 | stream.push(nil) 34 | end 35 | )() 36 | 37 | coroutine.wrap( 38 | function() 39 | local i = 0 40 | for item in read do 41 | if random() % 2 == 1 then 42 | pause() 43 | end 44 | i = i + 1 45 | assert(item == "item-" .. i, "Item out of order on test " .. index) 46 | end 47 | assert(i == 100, "expected 100 " .. index) 48 | print("Passed " .. index) 49 | end 50 | )() 51 | end 52 | 53 | for i = 1, 1000 do 54 | test(i) 55 | end 56 | -------------------------------------------------------------------------------- /tests/test-uv-ffi.lua: -------------------------------------------------------------------------------- 1 | local loop = require 'uv-ffi' 2 | local wrapStream = require 'wrap-stream' 3 | local httpCodec = require 'http-codec' 4 | 5 | local function main() 6 | local client = loop:newTcp() 7 | 8 | print('Connecting...') 9 | local res = assert(loop:getaddrinfo('luvit.io', 'http', {socktype = 'stream'})[1]) 10 | p(res) 11 | local addr = res.addr 12 | local port = res.port 13 | client:connect(addr, port) 14 | print('Connected!') 15 | 16 | local read, write, close = wrapStream(client) 17 | local encode = httpCodec.encoder() 18 | local decode = httpCodec.decoder() 19 | 20 | print('Writing') 21 | write( 22 | encode { 23 | method = 'GET', 24 | path = '/', 25 | {'Host', 'localhost:8080'}, 26 | {'User-Agent', 'luvit'}, 27 | {'Accept', '*.*'}, 28 | {'Connection', 'close'} 29 | } 30 | ) 31 | print('reading') 32 | local buffer = '' 33 | local index = 0 34 | while true do 35 | local out, newIndex = decode(buffer, index) 36 | if out then 37 | if out == '' then 38 | break 39 | end 40 | p(out) 41 | local timer = loop:newTimer() 42 | timer:sleep(100) 43 | timer:close() 44 | index = newIndex 45 | else 46 | local chunk = read() 47 | if not chunk then 48 | break 49 | end 50 | buffer = buffer .. chunk 51 | end 52 | end 53 | 54 | write() 55 | end 56 | 57 | coroutine.wrap( 58 | function() 59 | local success, error = xpcall(main, debug.traceback) 60 | if not success then 61 | print(error) 62 | os.exit(-1) 63 | end 64 | end 65 | )() 66 | 67 | loop:run 'DEFAULT' 68 | collectgarbage() 69 | collectgarbage() 70 | loop:run 'DEFAULT' 71 | 72 | loop:walk( 73 | function(handle) 74 | p('auto-closing handle', handle) 75 | if not handle:isClosing() then 76 | handle:close() 77 | end 78 | end 79 | ) 80 | 81 | loop:run 'DEFAULT' 82 | 83 | loop:close() 84 | -------------------------------------------------------------------------------- /tests/test-varint.lua: -------------------------------------------------------------------------------- 1 | local Varint = require 'varint' 2 | local deframe = Varint.deframe 3 | local frame = Varint.frame 4 | local decode = Varint.decode 5 | local encode = Varint.encode 6 | local Utils = require './utils' 7 | local listIter = Utils.listIter 8 | local iterList = Utils.iterList 9 | local listEqual = Utils.listEqual 10 | 11 | local function testDeframe(inputs, outputs) 12 | local results = iterList(deframe(listIter(inputs))) 13 | local passed = listEqual(results, outputs) 14 | p('INPUT ', inputs) 15 | p('EXPECTED', outputs) 16 | p('ACTUAL', results) 17 | p('passed', passed) 18 | if not passed then 19 | error 'testDeframe case failed!' 20 | end 21 | end 22 | 23 | -- input is result table, output is write function 24 | local function resultWriter(result) 25 | local size = 0 26 | return function(value) 27 | size = size + 1 28 | result[size] = value 29 | end 30 | end 31 | 32 | local function listWriter(list, write) 33 | for i = 1, #list do 34 | write(list[i]) 35 | end 36 | end 37 | 38 | local function testFrame(inputs, outputs) 39 | local results = {} 40 | listWriter(inputs, frame(resultWriter(results))) 41 | local passed = listEqual(results, outputs) 42 | p('INPUT ', inputs) 43 | p('EXPECTED', outputs) 44 | p('ACTUAL', results) 45 | p('passed', passed) 46 | if not passed then 47 | error 'testFrame case failed!' 48 | end 49 | end 50 | 51 | assert(decode('\x00', 1) == 0) 52 | assert(decode('\x33', 1) == 0x33) 53 | assert(decode('\x7f', 1) == 127) 54 | assert(decode('\x80\x01', 1) == 128) 55 | assert(decode('\x81\x01', 1) == 129) 56 | assert(decode('\xac\x02', 1) == 300) 57 | 58 | assert(encode(0) == '\x00') 59 | assert(encode(0x33) == '\x33') 60 | assert(encode(127) == '\x7f') 61 | assert(encode(128) == '\x80\x01') 62 | assert(encode(129) == '\x81\x01') 63 | assert(encode(300) == '\xac\x02') 64 | 65 | testFrame({'1234567890'}, {'\x0a', '1234567890'}) 66 | testFrame({'1234567890', '123'}, {'\x0a', '1234567890', '\x03', '123'}) 67 | testFrame({string.rep('X', 128)}, {'\x80\x01' , string.rep('X', 128)}) 68 | testFrame({string.rep('X', 300)}, {'\xac\x02' , string.rep('X', 300)}) 69 | 70 | testDeframe({'\x0a1234567890'}, {'1234567890'}) 71 | testDeframe({'\x0a123456789012'}, {'1234567890'}) 72 | testDeframe({'\x0a', '1234567890'}, {'1234567890'}) 73 | testDeframe({'\x0a1234', '567890'}, {'1234567890'}) 74 | testDeframe({'\x0a1', '234', '56789', '0'}, {'1234567890'}) 75 | testDeframe({'\x03123\x03123'}, {'123', '123'}) 76 | testDeframe({'\x03123\x03', '123'}, {'123', '123'}) 77 | testDeframe({'\x0312', '3\x03123'}, {'123', '123'}) 78 | testDeframe({'\x80\x01' .. string.rep('X', 128)}, {string.rep('X', 128)}) 79 | testDeframe({'\xac\x02' .. string.rep('X', 300)}, {string.rep('X', 300)}) 80 | -------------------------------------------------------------------------------- /tests/utils.lua: -------------------------------------------------------------------------------- 1 | local bit = require 'bit' 2 | local lshift = bit.lshift 3 | local rshift = bit.rshift 4 | local bxor = bit.bxor 5 | local defer = require 'defer' 6 | 7 | local Utils = {} 8 | 9 | -- A simple and fast deterministic random sequence generator. 10 | function Utils.deadbeef(seed) 11 | local beef = 0xdeadbeef 12 | 13 | return function() 14 | seed = bxor(lshift(seed, 7), rshift(seed, 25) + beef) 15 | beef = bxor(lshift(beef, 7), rshift(beef, 25) + 0xdeadbeef) 16 | return seed 17 | end 18 | end 19 | 20 | function Utils.mockStream() 21 | local queue = {} 22 | local size = 0 23 | local onRead 24 | local stream = {} 25 | 26 | local function flush() 27 | if onRead and size > 0 then 28 | local value = table.remove(queue, 1) 29 | size = size - 1 30 | pcall(onRead, nil, value) 31 | defer(flush) 32 | end 33 | end 34 | 35 | function stream.push(value) 36 | size = size + 1 37 | queue[size] = value 38 | defer(flush) 39 | end 40 | 41 | function stream:read_start(cb) 42 | -- p('read_start') 43 | onRead = cb 44 | defer(flush) 45 | end 46 | 47 | function stream:read_stop() 48 | -- p('read_stop') 49 | onRead = nil 50 | end 51 | 52 | return stream 53 | end 54 | 55 | -- Convert a list into an interator 56 | function Utils.listIter(t) 57 | local i = 0 58 | return function() 59 | i = i + 1 60 | return t[i] 61 | end 62 | end 63 | 64 | -- Convert an iterator into a list 65 | function Utils.iterList(it) 66 | local results = {} 67 | local i = 1 68 | for value in it do 69 | results[i] = value 70 | i = i + 1 71 | end 72 | return results 73 | end 74 | 75 | -- Return true if two lists are identical, false if not. 76 | function Utils.listEqual(a, b) 77 | if #a ~= #b then 78 | return false 79 | end 80 | for i = 1, #a do 81 | if a[i] ~= b[i] then 82 | return false 83 | end 84 | end 85 | return true 86 | end 87 | 88 | return Utils 89 | -------------------------------------------------------------------------------- /types.jack: -------------------------------------------------------------------------------- 1 | builtin-type Integer 2 | 3 | type Boolean = True | False 4 | 5 | type Rational(Integer, Integer) 6 | -------------------------------------------------------------------------------- /u8-array.lua: -------------------------------------------------------------------------------- 1 | -- Shared instance for many libraries 2 | return require 'ffi'.typeof 'uint8_t[?]' 3 | -------------------------------------------------------------------------------- /unused-libs/blowfish.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | ffi.cdef [[ 3 | typedef unsigned int BF_LONG; 4 | enum { 5 | BF_DECRYPT = 0, 6 | BF_ENCRYPT = 1, 7 | BF_ROUNDS = 16, 8 | BF_BLOCK = 8, 9 | }; 10 | typedef struct bf_key_st { 11 | BF_LONG P[BF_ROUNDS + 2]; 12 | BF_LONG S[4 * 256]; 13 | } BF_KEY; 14 | void BF_set_key(BF_KEY *key, int len, const unsigned char *data); 15 | void BF_ecb_encrypt(const unsigned char *in, unsigned char *out, 16 | BF_KEY *key, int enc); 17 | void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, 18 | long length, BF_KEY *schedule, unsigned char *ivec, int enc); 19 | void BF_cfb64_encrypt(const unsigned char *in, unsigned char *out, 20 | long length, BF_KEY *schedule, unsigned char *ivec, int *num, 21 | int enc); 22 | void BF_ofb64_encrypt(const unsigned char *in, unsigned char *out, 23 | long length, BF_KEY *schedule, unsigned char *ivec, int *num); 24 | const char *BF_options(void); 25 | void BF_encrypt(BF_LONG *data,const BF_KEY *key); 26 | void BF_decrypt(BF_LONG *data,const BF_KEY *key); 27 | ]] 28 | local BfKey = ffi.typeof('BF_KEY') 29 | 30 | local Blowfish = {} 31 | 32 | function Blowfish.encryptStream() 33 | end 34 | 35 | return Blowfish 36 | -------------------------------------------------------------------------------- /unused-libs/defer.lua: -------------------------------------------------------------------------------- 1 | local uv = require 'uv' 2 | local checker = uv.new_check() 3 | local idler = uv.new_idle() 4 | local deferQueue = {} 5 | local deferCount = 0 6 | 7 | local function onCheck() 8 | local queue = deferQueue 9 | local count = deferCount 10 | deferQueue = {} 11 | deferCount = 0 12 | for i = 1, count do 13 | queue[i]() 14 | end 15 | -- If the queue is still empty, we processed them all 16 | -- Turn the check hooks back off. 17 | if deferCount == 0 then 18 | checker:stop() 19 | idler:stop() 20 | end 21 | end 22 | 23 | return function(callback) 24 | -- If the queue was empty, the check hooks were disabled. 25 | -- Turn them back on. 26 | if deferCount == 0 then 27 | checker:start(onCheck) 28 | idler:start(onCheck) 29 | end 30 | 31 | deferCount = deferCount + 1 32 | deferQueue[deferCount] = callback 33 | end 34 | -------------------------------------------------------------------------------- /unused-libs/prety-print-ffi.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Copyright 2014-2015 The Luvit Authors. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS-IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --]] 18 | --[[lit-meta 19 | name = "luvit/pretty-print" 20 | version = "2.0.1" 21 | homepage = "https://github.com/luvit/luvit/blob/master/deps/pretty-print.lua" 22 | description = "A lua value pretty printer and colorizer for terminals." 23 | tags = {"colors", "tty"} 24 | license = "Apache 2" 25 | author = { name = "Tim Caswell" } 26 | ]] 27 | local loop = require 'uv-ffi' 28 | local getenv = require('os').getenv 29 | 30 | local prettyPrint, dump, strip, color, colorize, loadColors 31 | local theme = {} 32 | local useColors = false 33 | local defaultTheme 34 | 35 | local stdout, stdin, stderr, width 36 | 37 | local quote, quote2, dquote, dquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls 38 | 39 | local themes = { 40 | -- nice color theme using 16 ansi colors 41 | [16] = { 42 | property = '0;37', -- white 43 | sep = '1;30', -- bright-black 44 | braces = '1;30', -- bright-black 45 | ['nil'] = '1;30', -- bright-black 46 | boolean = '0;33', -- yellow 47 | number = '1;33', -- bright-yellow 48 | string = '0;32', -- green 49 | quotes = '1;32', -- bright-green 50 | escape = '1;32', -- bright-green 51 | ['function'] = '0;35', -- purple 52 | thread = '1;35', -- bright-purple 53 | table = '1;34', -- bright blue 54 | userdata = '1;36', -- bright cyan 55 | cdata = '0;36', -- cyan 56 | err = '1;31', -- bright red 57 | success = '1;33;42', -- bright-yellow on green 58 | failure = '1;33;41', -- bright-yellow on red 59 | highlight = '1;36;44' -- bright-cyan on blue 60 | }, 61 | -- nice color theme using ansi 256-mode colors 62 | [256] = { 63 | property = '38;5;253', 64 | braces = '38;5;247', 65 | sep = '38;5;240', 66 | ['nil'] = '38;5;244', 67 | boolean = '38;5;220', -- yellow-orange 68 | number = '38;5;202', -- orange 69 | string = '38;5;34', -- darker green 70 | quotes = '38;5;40', -- green 71 | escape = '38;5;46', -- bright green 72 | ['function'] = '38;5;129', -- purple 73 | thread = '38;5;199', -- pink 74 | table = '38;5;27', -- blue 75 | userdata = '38;5;39', -- blue2 76 | cdata = '38;5;69', -- teal 77 | err = '38;5;196', -- bright red 78 | success = '38;5;120;48;5;22', -- bright green on dark green 79 | failure = '38;5;215;48;5;52', -- bright red on dark red 80 | highlight = '38;5;45;48;5;236' -- bright teal on dark grey 81 | } 82 | } 83 | 84 | local special = { 85 | [7] = 'a', 86 | [8] = 'b', 87 | [9] = 't', 88 | [10] = 'n', 89 | [11] = 'v', 90 | [12] = 'f', 91 | [13] = 'r' 92 | } 93 | 94 | function strip(str) 95 | return string.gsub(str, '\027%[[^m]*m', '') 96 | end 97 | 98 | function loadColors(index) 99 | if index == nil then 100 | index = defaultTheme 101 | end 102 | 103 | -- Remove the old theme 104 | for key in pairs(theme) do 105 | theme[key] = nil 106 | end 107 | 108 | if index then 109 | local new = themes[index] 110 | if not new then 111 | error('Invalid theme index: ' .. tostring(index)) 112 | end 113 | -- Add the new theme 114 | for key in pairs(new) do 115 | theme[key] = new[key] 116 | end 117 | useColors = true 118 | else 119 | useColors = false 120 | end 121 | 122 | quote = colorize('quotes', "'", 'string') 123 | quote2 = colorize('quotes', "'") 124 | dquote = colorize('quotes', '"', 'string') 125 | dquote2 = colorize('quotes', '"') 126 | obrace = colorize('braces', '{ ') 127 | cbrace = colorize('braces', '}') 128 | obracket = colorize('property', '[') 129 | cbracket = colorize('property', ']') 130 | comma = colorize('sep', ', ') 131 | equals = colorize('sep', ' = ') 132 | 133 | controls = {} 134 | for i = 0, 31 do 135 | local c = special[i] 136 | if not c then 137 | if i < 10 then 138 | c = '00' .. tostring(i) 139 | else 140 | c = '0' .. tostring(i) 141 | end 142 | end 143 | controls[i] = colorize('escape', '\\' .. c, 'string') 144 | end 145 | controls[92] = colorize('escape', '\\\\', 'string') 146 | controls[34] = colorize('escape', '\\"', 'string') 147 | controls[39] = colorize('escape', "\\'", 'string') 148 | for i = 128, 255 do 149 | local c 150 | if i < 100 then 151 | c = '0' .. tostring(i) 152 | else 153 | c = tostring(i) 154 | end 155 | controls[i] = colorize('escape', '\\' .. c, 'string') 156 | end 157 | end 158 | 159 | function color(colorName) 160 | return '\27[' .. (theme[colorName] or '0') .. 'm' 161 | end 162 | 163 | function colorize(colorName, string, resetName) 164 | return useColors and (color(colorName) .. tostring(string) .. color(resetName)) or 165 | tostring(string) 166 | end 167 | 168 | local function stringEscape(c) 169 | return controls[string.byte(c, 1)] 170 | end 171 | 172 | function dump(value, recurse, nocolor) 173 | local seen = {} 174 | local output = {} 175 | local offset = 0 176 | local stack = {} 177 | 178 | local function recalcOffset(index) 179 | for i = index + 1, #output do 180 | local m = string.match(output[i], '\n([^\n]*)$') 181 | if m then 182 | offset = #(strip(m)) 183 | else 184 | offset = offset + #(strip(output[i])) 185 | end 186 | end 187 | end 188 | 189 | local function write(text, length) 190 | if not length then 191 | length = #(strip(text)) 192 | end 193 | -- Create room for data by opening parent blocks 194 | -- Start at the root and go down. 195 | local i = 1 196 | while offset + length > width and stack[i] do 197 | local entry = stack[i] 198 | if not entry.opened then 199 | entry.opened = true 200 | table.insert(output, entry.index + 1, '\n' .. string.rep(' ', i)) 201 | -- Recalculate the offset 202 | recalcOffset(entry.index) 203 | -- Bump the index of all deeper entries 204 | for j = i + 1, #stack do 205 | stack[j].index = stack[j].index + 1 206 | end 207 | end 208 | i = i + 1 209 | end 210 | output[#output + 1] = text 211 | offset = offset + length 212 | if offset > width then 213 | return dump(stack) 214 | end 215 | end 216 | 217 | local function indent() 218 | stack[#stack + 1] = { 219 | index = #output, 220 | opened = false 221 | } 222 | end 223 | 224 | local function unindent() 225 | stack[#stack] = nil 226 | end 227 | 228 | local function process(localValue) 229 | local typ = type(localValue) 230 | if typ == 'string' then 231 | if string.find(localValue, "'") and not string.find(localValue, '"') then 232 | write(dquote) 233 | write(string.gsub(localValue, '[%c\\\128-\255]', stringEscape)) 234 | write(dquote2) 235 | else 236 | write(quote) 237 | write(string.gsub(localValue, "[%c\\'\128-\255]", stringEscape)) 238 | write(quote2) 239 | end 240 | elseif typ == 'table' and not seen[localValue] then 241 | if not recurse then 242 | seen[localValue] = true 243 | end 244 | write(obrace) 245 | local i = 1 246 | -- Count the number of keys so we know when to stop adding commas 247 | local total = 0 248 | for _ in pairs(localValue) do 249 | total = total + 1 250 | end 251 | 252 | local nextIndex = 1 253 | for k, v in pairs(localValue) do 254 | indent() 255 | if k == nextIndex then 256 | -- if the key matches the last numerical index + 1 257 | -- This is how lists print without keys 258 | nextIndex = k + 1 259 | process(v) 260 | else 261 | if type(k) == 'string' and string.find(k, '^[%a_][%a%d_]*$') then 262 | write(colorize('property', k)) 263 | write(equals) 264 | else 265 | write(obracket) 266 | process(k) 267 | write(cbracket) 268 | write(equals) 269 | end 270 | if type(v) == 'table' then 271 | process(v) 272 | else 273 | indent() 274 | process(v) 275 | unindent() 276 | end 277 | end 278 | if i < total then 279 | write(comma) 280 | else 281 | write(' ') 282 | end 283 | i = i + 1 284 | unindent() 285 | end 286 | write(cbrace) 287 | else 288 | write(colorize(typ, tostring(localValue))) 289 | end 290 | end 291 | 292 | process(value) 293 | local s = table.concat(output) 294 | return nocolor and strip(s) or s 295 | end 296 | 297 | -- Print replacement that goes through libuv. This is useful on windows 298 | -- to use libuv's code to translate ansi escape codes to windows API calls. 299 | local oldPrint = _G.print 300 | function _G.print(...) 301 | local _, main = coroutine.running() 302 | if main then 303 | return oldPrint(...) 304 | end 305 | local n = select('#', ...) 306 | local arguments = {...} 307 | for i = 1, n do 308 | arguments[i] = tostring(arguments[i]) 309 | end 310 | stdout:write(table.concat(arguments, '\t') .. '\n') 311 | end 312 | 313 | function prettyPrint(...) 314 | local n = select('#', ...) 315 | local arguments = {...} 316 | for i = 1, n do 317 | arguments[i] = dump(arguments[i]) 318 | end 319 | print(unpack(arguments)) 320 | end 321 | 322 | if loop.guessHandle(0) == 'tty' then 323 | stdin = loop:newTty(0) 324 | else 325 | stdin = loop:newPipe(false) 326 | stdin:open(0) 327 | end 328 | 329 | if loop.guessHandle(1) == 'tty' then 330 | stdout = loop:newTty(1) 331 | width = stdout:getWinsize() 332 | if width == 0 then 333 | width = 80 334 | end 335 | -- auto-detect when 16 color mode should be used 336 | local term = getenv('TERM') 337 | if term and (term == 'xterm' or term:find '-256color$') then 338 | defaultTheme = 256 339 | else 340 | defaultTheme = 16 341 | end 342 | else 343 | stdout = loop:newPipe(false) 344 | stdout:open(1) 345 | width = 80 346 | end 347 | loadColors() 348 | 349 | if loop.guessHandle(2) == 'tty' then 350 | stderr = loop:newTty(2) 351 | else 352 | stderr = loop:newPipe(false) 353 | stderr:open(2) 354 | end 355 | 356 | return { 357 | loadColors = loadColors, 358 | theme = theme, 359 | print = print, 360 | prettyPrint = prettyPrint, 361 | dump = dump, 362 | color = color, 363 | colorize = colorize, 364 | stdin = stdin, 365 | stdout = stdout, 366 | stderr = stderr, 367 | strip = strip 368 | } 369 | -------------------------------------------------------------------------------- /unused-libs/socket-ffi.lua: -------------------------------------------------------------------------------- 1 | local loop = require 'uv-ffi' 2 | 3 | local function normalize(options) 4 | local t = type(options) 5 | if t == 'string' then 6 | options = {path = options} 7 | elseif t == 'number' then 8 | options = {port = options} 9 | elseif t ~= 'table' then 10 | assert('Net options must be table, string, or number') 11 | end 12 | if options.port or options.host then 13 | options.isTcp = true 14 | options.host = options.host or '127.0.0.1' 15 | assert(options.port, 'options.port is required for tcp connections') 16 | elseif options.path then 17 | options.isTcp = false 18 | else 19 | error('Must set either options.path or options.port') 20 | end 21 | return options 22 | end 23 | 24 | return function(options) 25 | local socket 26 | options = normalize(options) 27 | if options.isTcp then 28 | local res = 29 | assert( 30 | loop:getaddrinfo( 31 | options.host, 32 | options.port, 33 | { 34 | socktype = options.socktype, 35 | family = options.family 36 | } 37 | )[1] 38 | ) 39 | socket = loop:newTcp() 40 | socket:connect(res.addr, res.port) 41 | else 42 | socket = loop:newPipe(false) 43 | socket:connect(options.path) 44 | end 45 | return socket 46 | end 47 | -------------------------------------------------------------------------------- /unused-libs/stream-wrap-ffi.lua: -------------------------------------------------------------------------------- 1 | local Connection = require 'connection' 2 | 3 | -- This is a simple wrapper around raw libuv streams that lets 4 | -- us have pull-style streams with a nice coroutine based interface. 5 | -- Read calls will block till there is data. 6 | -- Write calls will block will the buffer is no longer full (applying backpressure). 7 | -- The read calls will automatically pause and resume the read stream to apply 8 | -- backpressure to the remote writer as well. 9 | return function(socket, onError) 10 | local paused = true 11 | local stream = Connection.newPush() 12 | local cb 13 | 14 | local function onRead(error, value) 15 | -- p('in', value) 16 | if error and onError then 17 | onError(error) 18 | end 19 | return stream.onChunk(value) 20 | end 21 | 22 | function stream.onStart() 23 | if not paused then 24 | return 25 | end 26 | paused = false 27 | cb = socket:readStart(cb or onRead) 28 | end 29 | 30 | function stream.onStop() 31 | if paused then 32 | return 33 | end 34 | paused = true 35 | socket:readStop() 36 | end 37 | 38 | function stream.writeChunk(value) 39 | -- p('out', value) 40 | if value then 41 | socket:write(value) 42 | else 43 | socket:shutdown() 44 | end 45 | end 46 | 47 | stream.socket = socket 48 | return stream 49 | end 50 | -------------------------------------------------------------------------------- /unused-libs/uv-ffi.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | local cast = ffi.cast 3 | local C = ffi.C 4 | local UV = ffi.load('uv') 5 | 6 | if ffi.os == 'Windows' then 7 | ffi.cdef [[ 8 | typedef struct uv_buf_t { 9 | ULONG len; 10 | char* base; 11 | } uv_buf_t; 12 | ]] 13 | else 14 | ffi.cdef [[ 15 | typedef struct uv_buf_t { 16 | char* base; 17 | size_t len; 18 | } uv_buf_t; 19 | ]] 20 | end 21 | 22 | ffi.cdef [[ 23 | uv_buf_t uv_buf_init(char* base, unsigned int len); 24 | 25 | typedef enum { 26 | UV_EOF = -4095 27 | } uv_errno_t; 28 | 29 | typedef enum { 30 | UV_UNKNOWN_HANDLE = 0, 31 | UV_ASYNC, 32 | UV_CHECK, 33 | UV_FS_EVENT, 34 | UV_FS_POLL, 35 | UV_HANDLE, 36 | UV_IDLE, 37 | UV_NAMED_PIPE, 38 | UV_POLL, 39 | UV_PREPARE, 40 | UV_PROCESS, 41 | UV_STREAM, 42 | UV_TCP, 43 | UV_TIMER, 44 | UV_TTY, 45 | UV_UDP, 46 | UV_SIGNAL, 47 | UV_FILE, 48 | UV_HANDLE_TYPE_MAX 49 | } uv_handle_type; 50 | 51 | typedef enum { 52 | UV_UNKNOWN_REQ = 0, 53 | UV_REQ, 54 | UV_CONNECT, 55 | UV_WRITE, 56 | UV_SHUTDOWN, 57 | UV_UDP_SEND, 58 | UV_FS, 59 | UV_WORK, 60 | UV_GETADDRINFO, 61 | UV_GETNAMEINFO, 62 | UV_REQ_TYPE_MAX, 63 | } uv_req_type; 64 | 65 | size_t uv_loop_size(void); 66 | size_t uv_req_size(uv_req_type type); 67 | size_t uv_handle_size(uv_handle_type type); 68 | ]] 69 | 70 | ffi.cdef( 71 | string.format( 72 | [[ 73 | struct uv_loop_s {uint8_t _[%d];}; 74 | struct uv_connect_s {uint8_t _[%d];}; 75 | struct uv_write_s {uint8_t _[%d];}; 76 | struct uv_shutdown_s {uint8_t _[%d];}; 77 | struct uv_getaddrinfo_s {uint8_t _[%d];}; 78 | struct uv_tcp_s {uint8_t _[%d];}; 79 | struct uv_tty_s {uint8_t _[%d];}; 80 | struct uv_pipe_s {uint8_t _[%d];}; 81 | struct uv_timer_s {uint8_t _[%d];}; 82 | ]], 83 | tonumber(UV.uv_loop_size()), 84 | tonumber(UV.uv_req_size(UV.UV_CONNECT)), 85 | tonumber(UV.uv_req_size(UV.UV_WRITE)), 86 | tonumber(UV.uv_req_size(UV.UV_SHUTDOWN)), 87 | tonumber(UV.uv_req_size(UV.UV_GETADDRINFO)), 88 | tonumber(UV.uv_handle_size(UV.UV_TCP)), 89 | tonumber(UV.uv_handle_size(UV.UV_TTY)), 90 | tonumber(UV.uv_handle_size(UV.UV_NAMED_PIPE)), 91 | tonumber(UV.uv_handle_size(UV.UV_TIMER)) 92 | ) 93 | ) 94 | 95 | ffi.cdef [[ 96 | typedef struct uv_loop_s uv_loop_t; 97 | typedef struct uv_req_s uv_req_t; 98 | typedef struct uv_write_s uv_write_t; 99 | typedef struct uv_connect_s uv_connect_t; 100 | typedef struct uv_shutdown_s uv_shutdown_t; 101 | typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; 102 | typedef struct uv_handle_s uv_handle_t; 103 | typedef struct uv_stream_s uv_stream_t; 104 | typedef struct uv_tcp_s uv_tcp_t; 105 | typedef struct uv_tty_s uv_tty_t; 106 | typedef struct uv_pipe_s uv_pipe_t; 107 | typedef struct uv_timer_s uv_timer_t; 108 | 109 | typedef enum uv_run_mode_e { 110 | UV_RUN_DEFAULT = 0, 111 | UV_RUN_ONCE, 112 | UV_RUN_NOWAIT 113 | } uv_run_mode; 114 | 115 | int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr); 116 | int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr); 117 | int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); 118 | int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size); 119 | int uv_inet_ntop(int af, const void* src, char* dst, size_t size); 120 | int uv_inet_pton(int af, const char* src, void* dst); 121 | 122 | const char* uv_err_name(int err); 123 | const char* uv_strerror(int err); 124 | ]] 125 | 126 | local function makeCallback(type) 127 | local thread = coroutine.running() 128 | local cb 129 | cb = 130 | cast( 131 | type, 132 | function(...) 133 | -- print('oncall', ...) 134 | cb:free() 135 | assert(coroutine.resume(thread, ...)) 136 | end 137 | ) 138 | return cb 139 | end 140 | 141 | local function uvGetError(status) 142 | return ffi.string(UV.uv_err_name(status)) .. ': ' .. ffi.string(UV.uv_strerror(status)) 143 | end 144 | 145 | local function uvCheck(status) 146 | if status < 0 then 147 | error(uvGetError(status)) 148 | else 149 | return status 150 | end 151 | end 152 | 153 | local Loop = {} 154 | 155 | ------------------------------------------------------------------------------- 156 | -- Req 157 | ------------------------------------------------------------------------------- 158 | 159 | ffi.cdef [[ 160 | int uv_cancel(uv_req_t* req); 161 | uv_req_type uv_req_get_type(const uv_req_t* req); 162 | const char* uv_req_type_name(uv_req_type type); 163 | ]] 164 | 165 | local Req = {} 166 | 167 | function Req:cancel() 168 | return uvCheck(UV.uv_cancel(cast('uv_req_t*', self))) 169 | end 170 | 171 | function Req:getType() 172 | local id = UV.uv_req_get_type(cast('uv_req_t*', self)) 173 | return ffi.string(UV.uv_req_type_name(id)) 174 | end 175 | 176 | ------------------------------------------------------------------------------- 177 | -- Connect 178 | ------------------------------------------------------------------------------- 179 | 180 | local Connect = setmetatable({}, {__index = Req}) 181 | Connect.type = ffi.typeof 'uv_connect_t' 182 | ffi.metatype(Connect.type, {__index = Connect}) 183 | function Connect.new() 184 | return Connect.type() 185 | end 186 | 187 | ------------------------------------------------------------------------------- 188 | -- Write 189 | ------------------------------------------------------------------------------- 190 | 191 | local Write = setmetatable({}, {__index = Req}) 192 | Write.type = ffi.typeof 'uv_write_t' 193 | ffi.metatype(Write.type, {__index = Write}) 194 | function Write.new() 195 | return Write.type() 196 | end 197 | 198 | ------------------------------------------------------------------------------- 199 | -- Shutdown 200 | ------------------------------------------------------------------------------- 201 | 202 | local Shutdown = setmetatable({}, {__index = Req}) 203 | Shutdown.type = ffi.typeof 'uv_shutdown_t' 204 | ffi.metatype(Shutdown.type, {__index = Shutdown}) 205 | function Shutdown.new() 206 | return Shutdown.type() 207 | end 208 | 209 | ------------------------------------------------------------------------------- 210 | -- Getaddrinfo 211 | ------------------------------------------------------------------------------- 212 | 213 | ffi.cdef [[ 214 | enum { 215 | AF_UNSPEC = 0, 216 | AF_INET = 2, 217 | AF_INET6 = 10 218 | }; 219 | enum { 220 | SOCK_STREAM = 1, 221 | SOCK_DGRAM = 2 222 | }; 223 | 224 | typedef int32_t socklen_t; 225 | 226 | struct addrinfo { 227 | int ai_flags; 228 | int ai_family; 229 | int ai_socktype; 230 | int ai_protocol; 231 | socklen_t ai_addrlen; 232 | struct sockaddr *ai_addr; 233 | char *ai_canonname; 234 | struct addrinfo *ai_next; 235 | }; 236 | 237 | typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res); 238 | 239 | int uv_getaddrinfo( 240 | uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb, 241 | const char* node, const char* service, const struct addrinfo* hints 242 | ); 243 | void uv_freeaddrinfo(struct addrinfo* ai); 244 | ]] 245 | 246 | local Getaddrinfo = setmetatable({}, {__index = Req}) 247 | Getaddrinfo.type = ffi.typeof 'uv_getaddrinfo_t' 248 | ffi.metatype(Getaddrinfo.type, {__index = Getaddrinfo}) 249 | function Getaddrinfo.new() 250 | return Getaddrinfo.type() 251 | end 252 | 253 | local families = { 254 | inet = UV.AF_INET, 255 | inet6 = UV.AF_INET6, 256 | [UV.AF_INET] = 'inet', 257 | [UV.AF_INET6] = 'inet6' 258 | } 259 | 260 | local socktypes = { 261 | stream = UV.SOCK_STREAM, 262 | dgram = UV.SOCK_DGRAM, 263 | [UV.SOCK_STREAM] = 'stream', 264 | [UV.SOCK_DGRAM] = 'dgram' 265 | } 266 | 267 | function Loop:getaddrinfo(node, service, hints) 268 | local req = Getaddrinfo.new() 269 | p(req) 270 | local opts = ffi.new('struct addrinfo') 271 | if type(service) == 'number' then 272 | service = tostring(service) 273 | end 274 | if hints then 275 | if hints.family then 276 | opts.ai_family = assert(families[hints.family], 'Unknown family name') 277 | end 278 | if hints.socktype then 279 | opts.ai_socktype = assert(socktypes[hints.socktype], 'Unknown socktype name') 280 | end 281 | end 282 | p(node, service, hints) 283 | uvCheck(UV.uv_getaddrinfo(self, req, makeCallback 'uv_getaddrinfo_cb', node, service, opts)) 284 | local _, status, res = coroutine.yield() 285 | uvCheck(status) 286 | local results = {} 287 | local i = 1 288 | while res ~= nil do 289 | local family = families[res.ai_family] 290 | local socktype = socktypes[res.ai_socktype] 291 | local entry = { 292 | family = family, 293 | socktype = socktype 294 | } 295 | results[i] = entry 296 | i = i + 1 297 | if family == 'inet' then 298 | local buf = ffi.new 'char[16]' 299 | local addr = cast('const struct sockaddr_in*', res.ai_addr) 300 | uvCheck(UV.uv_ip4_name(addr, buf, 16)) 301 | entry.addr = ffi.string(buf) 302 | entry.port = C.ntohs(addr.sin_port) 303 | elseif family == 'inet6' then 304 | local buf = ffi.new 'char[46]' 305 | local addr = cast('const struct sockaddr_in6*', res.ai_addr) 306 | uvCheck(UV.uv_ip6_name(addr, buf, 46)) 307 | entry.addr = ffi.string(buf) 308 | entry.port = C.ntohs(addr.sin6_port) 309 | end 310 | res = res.ai_next 311 | end 312 | return results 313 | end 314 | 315 | ------------------------------------------------------------------------------- 316 | -- Handle 317 | ------------------------------------------------------------------------------- 318 | 319 | ffi.cdef [[ 320 | typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg); 321 | typedef void (*uv_close_cb)(uv_handle_t* handle); 322 | typedef void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); 323 | 324 | struct sockaddr { 325 | unsigned short sa_family; 326 | char sa_data[14]; 327 | }; 328 | 329 | struct in_addr { 330 | unsigned long s_addr; 331 | }; 332 | 333 | struct sockaddr_in { 334 | short sin_family; 335 | unsigned short sin_port; 336 | struct in_addr sin_addr; 337 | char sin_zero[8]; 338 | }; 339 | 340 | struct in6_addr { 341 | unsigned char s6_addr[16]; 342 | }; 343 | 344 | struct sockaddr_in6 { 345 | uint16_t sin6_family; 346 | uint16_t sin6_port; 347 | uint32_t sin6_flowinfo; 348 | struct in6_addr sin6_addr; 349 | uint32_t sin6_scope_id; 350 | }; 351 | 352 | typedef uint16_t sa_family_t; 353 | 354 | struct sockaddr_storage { 355 | sa_family_t ss_family; 356 | char __ss_pad1[6]; 357 | int64_t __ss_align; 358 | char __ss_pad2[112]; 359 | }; 360 | 361 | uint32_t htonl(uint32_t hostlong); 362 | uint16_t htons(uint16_t hostshort); 363 | uint32_t ntohl(uint32_t netlong); 364 | uint16_t ntohs(uint16_t netshort); 365 | 366 | void *malloc(size_t size); 367 | void free(void *ptr); 368 | 369 | typedef int uv_file; 370 | 371 | uv_handle_type uv_guess_handle(uv_file file); 372 | 373 | int uv_is_active(const uv_handle_t* handle); 374 | int uv_is_closing(const uv_handle_t* handle); 375 | void uv_close(uv_handle_t* handle, uv_close_cb close_cb); 376 | void uv_ref(uv_handle_t* handle); 377 | void uv_unref(uv_handle_t* handle); 378 | int uv_has_ref(const uv_handle_t* handle); 379 | int uv_send_buffer_size(uv_handle_t* handle, int* value); 380 | int uv_recv_buffer_size(uv_handle_t* handle, int* value); 381 | uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle); 382 | void* uv_handle_get_data(const uv_handle_t* handle); 383 | void* uv_handle_set_data(uv_handle_t* handle, void* data); 384 | uv_handle_type uv_handle_get_type(const uv_handle_t* handle); 385 | const char* uv_handle_type_name(uv_handle_type type); 386 | ]] 387 | 388 | local function onAlloc(handle, suggestedSize, buf) 389 | local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle)) 390 | if cached ~= nil then 391 | buf.base = cached.base 392 | buf.len = cached.len 393 | else 394 | local base = C.malloc(suggestedSize) 395 | buf.base = base 396 | buf.len = suggestedSize 397 | -- Store the data in handle->data as a uv_buf_t* 398 | local data = cast('uv_buf_t*', C.malloc(ffi.sizeof 'uv_buf_t')) 399 | data.base = base 400 | data.len = suggestedSize 401 | UV.uv_handle_set_data(handle, data) 402 | end 403 | end 404 | 405 | local allocCb = cast('uv_alloc_cb', onAlloc) 406 | 407 | function Loop.guessHandle(fd) 408 | return ffi.string(UV.uv_handle_type_name(UV.uv_guess_handle(fd))) 409 | end 410 | 411 | local Handle = {} 412 | 413 | function Handle:isActive() 414 | return UV.uv_is_active(cast('uv_handle_t*', self)) ~= 0 415 | end 416 | 417 | function Handle:isClosing() 418 | return UV.uv_is_closing(cast('uv_handle_t*', self)) ~= 0 419 | end 420 | 421 | function Handle:close() 422 | local handle = cast('uv_handle_t*', self) 423 | local _, main = coroutine.running() 424 | if main then 425 | UV.uv_close(handle, nil) 426 | else 427 | UV.uv_close(handle, makeCallback 'uv_close_cb') 428 | coroutine.yield() 429 | end 430 | local cached = cast('uv_buf_t*', UV.uv_handle_get_data(handle)) 431 | if cached ~= nil then 432 | C.free(cached.base) 433 | C.free(cached) 434 | end 435 | end 436 | 437 | function Handle:ref() 438 | return UV.uv_ref(cast('uv_handle_t*', self)) 439 | end 440 | 441 | function Handle:unref() 442 | return UV.uv_unref(cast('uv_handle_t*', self)) 443 | end 444 | 445 | function Handle:hasRef() 446 | return UV.uv_has_ref(cast('uv_handle_t*', self)) ~= 0 447 | end 448 | 449 | function Handle:setSendBufferSize(value) 450 | uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), value)) 451 | end 452 | 453 | function Handle:getSendBufferSize() 454 | local out = ffi.new('int[1]') 455 | uvCheck(UV.uv_send_buffer_size(cast('uv_handle_t*', self), out)) 456 | return out[0] 457 | end 458 | 459 | function Handle:setRecvBufferSize(value) 460 | uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), value)) 461 | end 462 | 463 | function Handle:getRecvBufferSize() 464 | local out = ffi.new('int[1]') 465 | uvCheck(UV.uv_recv_buffer_size(cast('uv_handle_t*', self), out)) 466 | return out[0] 467 | end 468 | 469 | function Handle:getLoop() 470 | return UV.uv_handle_get_loop(cast('uv_handle_t*', self)) 471 | end 472 | 473 | function Handle:getType() 474 | local id = UV.uv_handle_get_type(cast('uv_handle_t*', self)) 475 | return ffi.string(UV.uv_handle_type_name(id)) 476 | end 477 | 478 | ------------------------------------------------------------------------------- 479 | -- Stream 480 | ------------------------------------------------------------------------------- 481 | 482 | ffi.cdef [[ 483 | typedef void (*uv_read_cb)(uv_stream_t* stream, int64_t nread, const uv_buf_t* buf); 484 | typedef void (*uv_write_cb)(uv_write_t* req, int status); 485 | typedef void (*uv_connect_cb)(uv_connect_t* req, int status); 486 | typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); 487 | typedef void (*uv_connection_cb)(uv_stream_t* server, int status); 488 | 489 | int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb); 490 | int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); 491 | int uv_accept(uv_stream_t* server, uv_stream_t* client); 492 | int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb); 493 | int uv_read_stop(uv_stream_t*); 494 | int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); 495 | int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, 496 | uv_stream_t* send_handle, uv_write_cb cb); 497 | int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); 498 | int uv_is_readable(const uv_stream_t* handle); 499 | int uv_is_writable(const uv_stream_t* handle); 500 | int uv_stream_set_blocking(uv_stream_t* handle, int blocking); 501 | size_t uv_stream_get_write_queue_size(const uv_stream_t* stream); 502 | ]] 503 | 504 | local Stream = setmetatable({}, {__index = Handle}) 505 | 506 | function Stream:shutdown() 507 | local req = Shutdown.new() 508 | uvCheck(UV.uv_shutdown(req, cast('uv_stream_t*', self), makeCallback 'uv_shutdown_cb')) 509 | local _, status = coroutine.yield() 510 | uvCheck(status) 511 | end 512 | 513 | function Stream:listen(backlog, onConnection) 514 | local cb = cast('uv_connection_cb', onConnection) 515 | uvCheck(UV.uv_listen(cast('uv_stream_t*', self), backlog, cb)) 516 | return cb 517 | end 518 | 519 | function Stream:accept(client) 520 | uvCheck(UV.uv_accept(cast('uv_stream_t*', self), cast('uv_stream_t*', client))) 521 | end 522 | 523 | function Stream:readStart(onRead) 524 | local function onEvent(_, status, buf) 525 | print('onRead', _, status, buf) 526 | if status == 0 then 527 | return 528 | end 529 | if status == UV.UV_EOF then 530 | onRead(nil) 531 | elseif status < 0 then 532 | return onRead(uvGetError(status)) 533 | else 534 | onRead(nil, ffi.string(buf.base, status)) 535 | end 536 | end 537 | local cb = cast('uv_read_cb', onEvent) 538 | uvCheck(UV.uv_read_start(cast('uv_stream_t*', self), allocCb, cb)) 539 | end 540 | 541 | function Stream:readStop() 542 | uvCheck(UV.uv_read_stop(cast('uv_stream_t*', self))) 543 | end 544 | 545 | function Stream:write(data) 546 | local req = Write.new() 547 | local bufs = ffi.new('uv_buf_t[1]') 548 | bufs[0].base = cast('char*', data) 549 | bufs[0].len = #data 550 | local cb = makeCallback 'uv_write_cb' 551 | uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), bufs, 1, cb)) 552 | local _, status = coroutine.yield() 553 | uvCheck(status) 554 | end 555 | 556 | function Stream:write2(handle) 557 | local req = Write.new() 558 | local cb = makeCallback 'uv_write_cb' 559 | uvCheck(UV.uv_write(req, cast('uv_stream_t*', self), nil, 0, cast('uv_stream_t*', handle), cb)) 560 | local _, status = coroutine.yield() 561 | uvCheck(status) 562 | end 563 | 564 | function Stream:tryWrite(data) 565 | local bufs = ffi.new('uv_buf_t[1]') 566 | bufs[0].base = cast('char*', data) 567 | bufs[0].len = #data 568 | return uvCheck(UV.uv_try_write(cast('uv_stream_t*', self), bufs, 1)) 569 | end 570 | 571 | function Stream:isReadable() 572 | return UV.uv_is_readable(cast('uv_stream_t*', self)) ~= 0 573 | end 574 | 575 | function Stream:isWritable() 576 | return UV.uv_is_writable(cast('uv_stream_t*', self)) ~= 0 577 | end 578 | 579 | function Stream:setBlocking(blocking) 580 | uvCheck(UV.uv_set_blocking(cast('uv_stream_t*', self), blocking)) 581 | end 582 | 583 | function Stream:getWriteQueueSize() 584 | return tonumber(UV.uv_stream_get_write_queue_size(cast('uv_stream_t*', self))) 585 | end 586 | 587 | ------------------------------------------------------------------------------- 588 | -- Tcp 589 | ------------------------------------------------------------------------------- 590 | 591 | ffi.cdef [[ 592 | int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle); 593 | int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags); 594 | int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr_in* addr, uv_connect_cb cb); 595 | ]] 596 | 597 | local Tcp = setmetatable({}, {__index = Stream}) 598 | Tcp.type = ffi.typeof 'uv_tcp_t' 599 | 600 | function Loop:newTcp() 601 | local tcp = Tcp.type() 602 | uvCheck(UV.uv_tcp_init(self, tcp)) 603 | return tcp 604 | end 605 | 606 | function Tcp:getsockname() 607 | -- TODO: Improve output 608 | return UV.uv_tcp_getsockname(self) 609 | end 610 | 611 | function Tcp:getpeername() 612 | -- TODO: Improve output 613 | return UV.uv_tcp_getpeername(self) 614 | end 615 | 616 | function Tcp:connect(host, port) 617 | local req = Connect.new() 618 | local addr = ffi.new 'struct sockaddr_in' 619 | UV.uv_ip4_addr(host, port, addr) 620 | uvCheck(UV.uv_tcp_connect(req, self, addr, makeCallback 'uv_connect_cb')) 621 | local _, status = coroutine.yield() 622 | uvCheck(status) 623 | end 624 | 625 | ffi.metatype(Tcp.type, {__index = Tcp}) 626 | 627 | ------------------------------------------------------------------------------- 628 | -- Tty 629 | ------------------------------------------------------------------------------- 630 | 631 | ffi.cdef [[ 632 | typedef enum { 633 | /* Initial/normal terminal mode */ 634 | UV_TTY_MODE_NORMAL, 635 | /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ 636 | UV_TTY_MODE_RAW, 637 | /* Binary-safe I/O mode for IPC (Unix-only) */ 638 | UV_TTY_MODE_IO 639 | } uv_tty_mode_t; 640 | 641 | int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int unused); 642 | int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode); 643 | int uv_tty_reset_mode(void); 644 | int uv_tty_get_winsize(uv_tty_t* handle, int* width, int* height); 645 | ]] 646 | 647 | local Tty = setmetatable({}, {__index = Stream}) 648 | Tty.type = ffi.typeof 'uv_tty_t' 649 | 650 | function Loop:newTty(fd) 651 | local tty = Tty.type() 652 | uvCheck(UV.uv_tty_init(self, tty, fd, 0)) 653 | return tty 654 | end 655 | 656 | function Tty:setMode(mode) 657 | uvCheck( 658 | UV.uv_tty_set_mode(self, assert(UV['UV_TTY_MODE_' .. string.upper(mode)], 'Unknown tty mode')) 659 | ) 660 | end 661 | 662 | function Tty:resetMode() 663 | uvCheck(UV.uv_tty_reset_mode()) 664 | end 665 | 666 | function Tty:getWinsize() 667 | local width = ffi.new('int[1]') 668 | local height = ffi.new('int[1]') 669 | uvCheck(UV.uv_tty_get_winsize(self, width, height)) 670 | return tonumber(width[0]), tonumber(height[0]) 671 | end 672 | 673 | ffi.metatype(Tty.type, {__index = Tty}) 674 | 675 | ------------------------------------------------------------------------------- 676 | -- Pipe 677 | ------------------------------------------------------------------------------- 678 | 679 | ffi.cdef [[ 680 | int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc); 681 | int uv_pipe_open(uv_pipe_t* handle, uv_file file); 682 | int uv_pipe_bind(uv_pipe_t* handle, const char* name); 683 | void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb); 684 | int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size); 685 | int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size); 686 | void uv_pipe_pending_instances(uv_pipe_t* handle, int count); 687 | int uv_pipe_pending_count(uv_pipe_t* handle); 688 | uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); 689 | int uv_pipe_chmod(uv_pipe_t* handle, int flags); 690 | ]] 691 | 692 | local Pipe = setmetatable({}, {__index = Stream}) 693 | Pipe.type = ffi.typeof 'uv_pipe_t' 694 | 695 | function Loop:newPipe(ipc) 696 | local pipe = Pipe.type() 697 | uvCheck(UV.uv_pipe_init(self, pipe, ipc and 1 or 0)) 698 | return pipe 699 | end 700 | 701 | function Pipe:open(fd) 702 | uvCheck(UV.uv_pipe_open(self, fd)) 703 | end 704 | 705 | function Pipe:bind(path) 706 | uvCheck(UV.uv_pipe_bind(self, path)) 707 | end 708 | 709 | -- TODO: add more pipe functions 710 | 711 | ffi.metatype(Pipe.type, {__index = Pipe}) 712 | 713 | ------------------------------------------------------------------------------- 714 | -- Timer 715 | ------------------------------------------------------------------------------- 716 | 717 | ffi.cdef [[ 718 | typedef void (*uv_timer_cb)(uv_timer_t* handle); 719 | 720 | int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle); 721 | int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat); 722 | int uv_timer_stop(uv_timer_t* handle); 723 | int uv_timer_again(uv_timer_t* handle); 724 | void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat); 725 | uint64_t uv_timer_get_repeat(const uv_timer_t* handle); 726 | ]] 727 | 728 | local Timer = setmetatable({}, {__index = Handle}) 729 | Timer.Type = ffi.typeof 'uv_timer_t' 730 | 731 | function Loop:newTimer() 732 | local timer = Timer.Type() 733 | uvCheck(UV.uv_timer_init(self, timer)) 734 | return timer 735 | end 736 | 737 | function Timer:sleep(timeout) 738 | uvCheck(UV.uv_timer_start(self, makeCallback 'uv_timer_cb', timeout, 0)) 739 | coroutine.yield() 740 | uvCheck(UV.uv_timer_stop(self)) 741 | end 742 | 743 | function Timer:start(callback, timeout, rep) 744 | local cb = cast('uv_timer_cb', timeout) 745 | uvCheck(UV.uv_timer_start(self, callback, cb, rep)) 746 | return cb 747 | end 748 | 749 | function Timer:stop() 750 | uvCheck(UV.uv_timer_stop(self)) 751 | end 752 | 753 | function Timer:again() 754 | uvCheck(UV.uv_timer_again(self)) 755 | end 756 | 757 | function Timer:setRepeat(rep) 758 | uvCheck(UV.uv_timer_set_repeat(self, rep)) 759 | end 760 | 761 | function Timer:getRepeat() 762 | return UV.uv_timer_get_repeat(self) 763 | end 764 | 765 | local function onGc(handle) 766 | if not handle:isClosing() then 767 | -- We can't safely close handles here because onClose happens after the 768 | -- struct is freed by lua. 769 | -- Instead abort the process so the programmer can fix it early. 770 | handle = cast('uv_' .. Handle.getType(handle) .. '_t*', handle) 771 | error('Unclosed ' .. tostring(handle) .. ' got garbage collected') 772 | end 773 | end 774 | 775 | ffi.metatype(Timer.Type, {__index = Timer, __gc = onGc}) 776 | 777 | ------------------------------------------------------------------------------- 778 | -- Loop 779 | ------------------------------------------------------------------------------- 780 | 781 | ffi.cdef [[ 782 | uv_loop_t* uv_default_loop(); 783 | int uv_loop_init(uv_loop_t* loop); 784 | int uv_loop_close(uv_loop_t* loop); 785 | int uv_loop_alive(const uv_loop_t* loop); 786 | void uv_stop(uv_loop_t* loop); 787 | uint64_t uv_now(const uv_loop_t* loop); 788 | void uv_update_time(uv_loop_t* loop); 789 | void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg); 790 | int uv_run(uv_loop_t* loop, uv_run_mode mode); 791 | ]] 792 | 793 | local LoopType = ffi.typeof 'uv_loop_t' 794 | 795 | function Loop.new() 796 | local loop = LoopType() 797 | uvCheck(UV.uv_loop_init(loop)) 798 | return loop 799 | end 800 | 801 | function Loop:close() 802 | uvCheck(UV.uv_loop_close(self)) 803 | end 804 | 805 | function Loop:alive() 806 | return UV.uv_loop_alive(self) ~= 0 807 | end 808 | 809 | function Loop:stop() 810 | return UV.uv_loop_stop(self) 811 | end 812 | 813 | function Loop:now() 814 | return UV.uv_loop_now(self) 815 | end 816 | 817 | function Loop:updateTime() 818 | return UV.uv_update_time(self) 819 | end 820 | 821 | function Loop:walk(callback) 822 | local function onHandle(handle) 823 | callback(cast('uv_' .. Handle.getType(handle) .. '_t*', handle)) 824 | end 825 | local cb = cast('uv_walk_cb', onHandle) 826 | UV.uv_walk(self, cb, nil) 827 | cb:free() 828 | end 829 | 830 | function Loop:run(mode) 831 | mode = assert(UV['UV_RUN_' .. string.upper(mode)], 'Unknown run mode') 832 | return tonumber(uvCheck(UV.uv_run(self, mode))) 833 | end 834 | 835 | ffi.metatype(LoopType, {__index = Loop}) 836 | 837 | ------------------------------------------------------------------------------- 838 | 839 | Loop.Req = Req 840 | Loop.Connect = Connect 841 | Loop.Write = Write 842 | Loop.Shutdown = Shutdown 843 | Loop.Handle = Handle 844 | Loop.Stream = Stream 845 | Loop.Tcp = Tcp 846 | Loop.Timer = Timer 847 | 848 | return UV.uv_default_loop() 849 | -------------------------------------------------------------------------------- /unused-libs/wrap-stream.lua: -------------------------------------------------------------------------------- 1 | return function(stream) 2 | local writing = true 3 | local reading = true 4 | local closed = false 5 | local paused = true 6 | local queue = {} 7 | local reads = 0 8 | local writes = 0 9 | local cb 10 | 11 | local function close() 12 | assert(not closed) 13 | closed = true 14 | reading = false 15 | writing = false 16 | stream:close() 17 | if cb then 18 | cb:free() 19 | cb = nil 20 | end 21 | end 22 | 23 | local function checkClose() 24 | assert(not closed) 25 | if reading or writing then 26 | return 27 | end 28 | return close() 29 | end 30 | 31 | local function onRead(err, data) 32 | assert(not err, err) 33 | if not data then 34 | reading = false 35 | checkClose() 36 | end 37 | 38 | if reads > writes then 39 | local thread 40 | thread, queue[writes] = queue[writes], nil 41 | writes = writes + 1 42 | return coroutine.resume(thread, data) 43 | end 44 | 45 | if writes > reads and not paused then 46 | paused = true 47 | stream:readStop() 48 | end 49 | 50 | queue[writes] = data 51 | writes = writes + 1 52 | end 53 | 54 | local function read() 55 | assert(reading) 56 | local data 57 | if writes > reads then 58 | data, queue[reads] = queue[reads], nil 59 | reads = reads + 1 60 | else 61 | if paused then 62 | paused = false 63 | cb = stream:readStart(cb or onRead) 64 | end 65 | 66 | queue[reads] = coroutine.running() 67 | reads = reads + 1 68 | 69 | data = coroutine.yield() 70 | end 71 | return data 72 | end 73 | 74 | local function write(message) 75 | assert(writing) 76 | if message then 77 | stream:write(message) 78 | else 79 | writing = false 80 | stream:shutdown() 81 | checkClose() 82 | end 83 | end 84 | 85 | return read, write, close, stream 86 | end 87 | -------------------------------------------------------------------------------- /uv-socket.lua: -------------------------------------------------------------------------------- 1 | local uv = require "luv" 2 | local makeCallback = require "make-callback" 3 | 4 | local function normalize(options) 5 | local t = type(options) 6 | if t == "string" then 7 | options = {path = options} 8 | elseif t == "number" then 9 | options = {port = options} 10 | elseif t ~= "table" then 11 | assert("Net options must be table, string, or number") 12 | end 13 | if options.port or options.host then 14 | options.isTcp = true 15 | options.host = options.host or "127.0.0.1" 16 | assert(options.port, "options.port is required for tcp connections") 17 | elseif options.path then 18 | options.isTcp = false 19 | else 20 | error("Must set either options.path or options.port") 21 | end 22 | return options 23 | end 24 | 25 | return function(options) 26 | local socket, success, err 27 | options = normalize(options) 28 | if options.isTcp then 29 | success, err = 30 | uv.getaddrinfo( 31 | options.host, 32 | options.port, 33 | { 34 | socktype = options.socktype or "stream", 35 | family = options.family or "inet" 36 | }, 37 | makeCallback() 38 | ) 39 | if not success then 40 | return nil, err 41 | end 42 | local res 43 | res, err = coroutine.yield() 44 | if not res then 45 | return nil, err 46 | end 47 | socket = uv.new_tcp() 48 | socket:connect(res[1].addr, res[1].port, makeCallback()) 49 | else 50 | socket = uv.new_pipe(false) 51 | socket:connect(options.path, makeCallback()) 52 | end 53 | success, err = coroutine.yield() 54 | if not success then 55 | return nil, err 56 | end 57 | return socket 58 | end -------------------------------------------------------------------------------- /varint.lua: -------------------------------------------------------------------------------- 1 | local char = string.char 2 | local byte = string.byte 3 | local unpack = table.unpack or unpack 4 | local bit = require 'bit' 5 | local bor = bit.bor 6 | local band = bit.band 7 | local lshift = bit.lshift 8 | local rshift = bit.rshift 9 | 10 | local Varint = {} 11 | 12 | function Varint.decode(chunk, index) 13 | local length = 0 14 | local bits = 0 15 | while true do 16 | local b = byte(chunk, index) 17 | index = index + 1 18 | length = bor(length, lshift(band(b, 0x7f), bits)) 19 | if b < 0x80 then 20 | break 21 | end 22 | bits = bits + 7 23 | end 24 | return length, index 25 | end 26 | 27 | function Varint.decodebin(chunk, offset) 28 | local length = 0 29 | local bits = 0 30 | while true do 31 | local b = chunk[offset] 32 | offset = offset + 1 33 | length = bor(length, lshift(band(b, 0x7f), bits)) 34 | if b < 0x80 then 35 | break 36 | end 37 | bits = bits + 7 38 | end 39 | return length, offset 40 | end 41 | 42 | function Varint.encode(num) 43 | local parts = {} 44 | while num >= 0x80 do 45 | parts[#parts + 1] = bor(band(num, 0x7f), 0x80) 46 | num = rshift(num, 7) 47 | end 48 | parts[#parts + 1] = num 49 | return char(unpack(parts)) 50 | end 51 | 52 | local encode = Varint.encode 53 | 54 | function Varint.read(stream) 55 | -- Parse the varint length header first. 56 | local length = 0 57 | local bits = 0 58 | while true do 59 | local b = stream.readByte() 60 | if not b then 61 | return 62 | end 63 | length = bor(length, lshift(band(b, 0x7f), bits)) 64 | if b < 0x80 then 65 | break 66 | end 67 | bits = bits + 7 68 | end 69 | return length 70 | end 71 | 72 | local read = Varint.read 73 | 74 | function Varint.readFrame(stream) 75 | local length = read(stream) 76 | if not length then 77 | return 78 | end 79 | return stream.readChunk(length) 80 | end 81 | 82 | function Varint.write(stream, value) 83 | return stream.writeChunk(encode(value)) 84 | end 85 | 86 | function Varint.writeFrame(stream, message) 87 | stream.writeChunk(encode(#message) .. message) 88 | end 89 | 90 | return Varint 91 | --------------------------------------------------------------------------------