-
-
Save rphillips/8e996de28468b6a6209f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local openssl = require('openssl') | |
local tcp = require('coro-tcp') | |
local table = require('table') | |
local bit = require('bit') | |
local LOCAL_VERSION = "SSH-2.0-LUVIT-1" | |
local SUPPORTED_HOSTKEY_ALGOS = "ssh-rsa" | |
local SUPPORTED_MAC_MODE = "hmac-sha2-256" | |
local SUPPORTED_CIPHER_MODE = "aes256-ctr" | |
local SUPPORTED_KEX_ALGOS = "ecdh-sha2-nistp256" | |
local SUPPORTED_COMPRESSION_MODE = "none" | |
local function parseUint32(buffer, offset) | |
offset = offset or 1 | |
local num = bit.bor( | |
bit.lshift(string.byte(buffer, offset), 24), | |
bit.lshift(string.byte(buffer, offset + 1), 16), | |
bit.lshift(string.byte(buffer, offset + 2), 8), | |
string.byte(buffer, offset + 3) | |
) | |
return num, 4 | |
end | |
local function writeUint32(num) | |
return table.concat({ | |
string.char(bit.band(bit.rshift(num, 24), 0xff)), | |
string.char(bit.band(bit.rshift(num, 16), 0xff)), | |
string.char(bit.band(bit.rshift(num, 8), 0xff)), | |
string.char(bit.band(num, 0xff)), | |
}) | |
end | |
exports.client = function() | |
local rawRead, rawWrite, socket | |
local buffer = '' | |
local function exchangeVersion() | |
buffer = buffer .. rawRead() | |
local idx = buffer:find('\n') | |
if idx == -1 then return end | |
buffer = buffer:sub(idx + 1) | |
rawWrite(LOCAL_VERSION .. '\r\n') | |
end | |
local padLengthMultiple = 16 | |
local function send(payload) | |
payload = table.concat(payload) | |
local pktlen = #payload + 9 | |
pktlen = pktlen + ((padLengthMultiple - 1) * pktlen) % padLengthMultiple | |
local padLen = pktlen - #payload - 5 | |
local frame = { | |
writeUint32(pktlen - 4), | |
string.char(padLen), | |
payload, | |
openssl.random(padLen) | |
} | |
rawWrite(table.concat(frame)) | |
end | |
local function readKex() | |
local length, offsetAdd, offset, pktLen | |
buffer = buffer .. rawRead() | |
if #buffer < 20 then return end | |
pktLen, offsetAdd = parseUint32(buffer) | |
while #buffer < pktLen - 4 do | |
buffer = buffer.. rawRead() | |
end | |
local padLen = string.byte(buffer, 5) | |
local type = string.byte(buffer, 6) | |
local cookie = string.sub(buffer, 7, 22) | |
length, offsetAdd = parseUint32(buffer, 23) | |
offset = 23 + offsetAdd + length - 1 | |
local kexStr = buffer:sub(27, offset) | |
offset = offset + 1 | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local keyFormats = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local clientServerCiphers = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local serverClientCiphers = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local clientServerHMAC = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local serverClientHMAC = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local clientServerCompression = buffer:sub(offset, offset + length - 1) | |
offset = offset + length | |
length, offsetAdd = parseUint32(buffer, offset) | |
offset = offset + offsetAdd | |
local serverClientCompression = buffer:sub(offset, offset + length - 1) | |
buffer = buffer:sub(pktLen + 5) | |
p('type', type) | |
p('cookie', cookie) | |
p('kexStr', kexStr) | |
p('keyFormats', keyFormats) | |
p('clientServerCiphers', clientServerCiphers) | |
p('serverClientCiphers', serverClientCiphers) | |
p('clientServerHMAC', clientServerHMAC) | |
p('serverClientHMAC', serverClientHMAC) | |
p('clientServerCompression', clientServerCompression) | |
p('serverClientCompression', serverClientCompression) | |
local msg = { | |
string.char(20), | |
openssl.random(16), | |
writeUint32(#SUPPORTED_KEX_ALGOS), SUPPORTED_KEX_ALGOS, -- kexlist | |
writeUint32(#SUPPORTED_HOSTKEY_ALGOS), SUPPORTED_HOSTKEY_ALGOS, -- server host key algos | |
writeUint32(#SUPPORTED_CIPHER_MODE), SUPPORTED_CIPHER_MODE, -- cipher list | |
writeUint32(#SUPPORTED_CIPHER_MODE), SUPPORTED_CIPHER_MODE, -- cipher list | |
writeUint32(#SUPPORTED_MAC_MODE), SUPPORTED_MAC_MODE, -- hmac | |
writeUint32(#SUPPORTED_MAC_MODE), SUPPORTED_MAC_MODE, -- hmac | |
writeUint32(#SUPPORTED_COMPRESSION_MODE), SUPPORTED_COMPRESSION_MODE, -- compress | |
writeUint32(#SUPPORTED_COMPRESSION_MODE), SUPPORTED_COMPRESSION_MODE, -- compress | |
writeUint32(0), "", -- languages | |
writeUint32(0), "", -- languages | |
string.rep(string.char(0), 5) -- reserve | |
} | |
send(msg) | |
end | |
local function connect(options) | |
rawRead, rawWrite, socket = tcp.connect(options.host, options.port) | |
exchangeVersion() | |
readKex() | |
end | |
return { | |
connect = connect | |
} | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment