123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- #!/usr/local/bin/lua
- --
- -- Copyright (c) 2007, Cameron Rich
- --
- -- All rights reserved.
- --
- -- Redistribution and use in source and binary forms, with or without
- -- modification, are permitted provided that the following conditions are met:
- --
- -- * Redistributions of source code must retain the above copyright notice,
- -- this list of conditions and the following disclaimer.
- -- * Redistributions in binary form must reproduce the above copyright
- -- notice, this list of conditions and the following disclaimer in the
- -- documentation and/or other materials provided with the distribution.
- -- * Neither the name of the axTLS project nor the names of its
- -- contributors may be used to endorse or promote products derived
- -- from this software without specific prior written permission.
- --
- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- -- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- -- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- -- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- -- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- --
- --
- -- Demonstrate the use of the axTLS library in Lua with a set of
- -- command-line parameters similar to openssl. In fact, openssl clients
- -- should be able to communicate with axTLS servers and visa-versa.
- --
- -- This code has various bits enabled depending on the configuration. To enable
- -- the most interesting version, compile with the 'full mode' enabled.
- --
- -- To see what options you have, run the following:
- -- > [lua] axssl s_server -?
- -- > [lua] axssl s_client -?
- --
- -- The axtls/axtlsl shared libraries must be in the same directory or be found
- -- by the OS.
- --
- --
- require "bit"
- require("axtlsl")
- local socket = require("socket")
- -- print version?
- if #arg == 1 and arg[1] == "version" then
- print("axssl.lua "..axtlsl.ssl_version())
- os.exit(1)
- end
- --
- -- We've had some sort of command-line error. Print out the basic options.
- --
- function print_options(option)
- print("axssl: Error: '"..option.."' is an invalid command.")
- print("usage: axssl [s_server|s_client|version] [args ...]")
- os.exit(1)
- end
- --
- -- We've had some sort of command-line error. Print out the server options.
- --
- function print_server_options(build_mode, option)
- local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
- local ca_cert_size = axtlsl.ssl_get_config(
- axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
- print("unknown option "..option)
- print("usage: s_server [args ...]")
- print(" -accept\t- port to accept on (default is 4433)")
- print(" -quiet\t\t- No server output")
- if build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then
- print(" -cert arg\t- certificate file to add (in addition to "..
- "default) to chain -")
- print("\t\t Can repeat up to "..cert_size.." times")
- print(" -key arg\t- Private key file to use - default DER format")
- print(" -pass\t\t- private key file pass phrase source")
- end
- if build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then
- print(" -verify\t- turn on peer certificate verification")
- print(" -CAfile arg\t- Certificate authority - default DER format")
- print("\t\t Can repeat up to "..ca_cert_size.." times")
- end
- if build_mode == axtlsl.SSL_BUILD_FULL_MODE then
- print(" -debug\t\t- Print more output")
- print(" -state\t\t- Show state messages")
- print(" -show-rsa\t- Show RSA state")
- end
- os.exit(1)
- end
- --
- -- We've had some sort of command-line error. Print out the client options.
- --
- function print_client_options(build_mode, option)
- local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
- local ca_cert_size = axtlsl.ssl_get_config(
- axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
- print("unknown option "..option)
- if build_mode >= axtlsl.SSL_BUILD_ENABLE_CLIENT then
- print("usage: s_client [args ...]")
- print(" -connect host:port - who to connect to (default "..
- "is localhost:4433)")
- print(" -verify\t- turn on peer certificate verification")
- print(" -cert arg\t- certificate file to use - default DER format")
- print(" -key arg\t- Private key file to use - default DER format")
- print("\t\t Can repeat up to "..cert_size.." times")
- print(" -CAfile arg\t- Certificate authority - default DER format")
- print("\t\t Can repeat up to "..ca_cert_size.."times")
- print(" -quiet\t\t- No client output")
- print(" -pass\t\t- private key file pass phrase source")
- print(" -reconnect\t- Drop and re-make the connection "..
- "with the same Session-ID")
- if build_mode == axtlsl.SSL_BUILD_FULL_MODE then
- print(" -debug\t\t- Print more output")
- print(" -state\t\t- Show state messages")
- print(" -show-rsa\t- Show RSA state")
- end
- else
- print("Change configuration to allow this feature")
- end
- os.exit(1)
- end
- -- Implement the SSL server logic.
- function do_server(build_mode)
- local i = 2
- local v
- local port = 4433
- local options = axtlsl.SSL_DISPLAY_CERTS
- local quiet = false
- local password = ""
- local private_key_file = nil
- local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
- local ca_cert_size = axtlsl.
- ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
- local cert = {}
- local ca_cert = {}
- while i <= #arg do
- if arg[i] == "-accept" then
- if i >= #arg then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- port = arg[i]
- elseif arg[i] == "-quiet" then
- quiet = true
- options = bit.band(options, bit.bnot(axtlsl.SSL_DISPLAY_CERTS))
- elseif build_mode >= axtlsl.SSL_BUILD_SERVER_ONLY then
- if arg[i] == "-cert" then
- if i >= #arg or #cert >= cert_size then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- table.insert(cert, arg[i])
- elseif arg[i] == "-key" then
- if i >= #arg then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- private_key_file = arg[i]
- options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY)
- elseif arg[i] == "-pass" then
- if i >= #arg then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- password = arg[i]
- elseif build_mode >= axtlsl.SSL_BUILD_ENABLE_VERIFICATION then
- if arg[i] == "-verify" then
- options = bit.bor(options, axtlsl.SSL_CLIENT_AUTHENTICATION)
- elseif arg[i] == "-CAfile" then
- if i >= #arg or #ca_cert >= ca_cert_size then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- table.insert(ca_cert, arg[i])
- elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then
- if arg[i] == "-debug" then
- options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES)
- elseif arg[i] == "-state" then
- options = bit.bor(options, axtlsl.SSL_DISPLAY_STATES)
- elseif arg[i] == "-show-rsa" then
- options = bit.bor(options, axtlsl.SSL_DISPLAY_RSA)
- else
- print_server_options(build_mode, arg[i])
- end
- else
- print_server_options(build_mode, arg[i])
- end
- else
- print_server_options(build_mode, arg[i])
- end
- else
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- end
- -- Create socket for incoming connections
- local server_sock = socket.try(socket.bind("*", port))
- ---------------------------------------------------------------------------
- -- This is where the interesting stuff happens. Up until now we've
- -- just been setting up sockets etc. Now we do the SSL handshake.
- ---------------------------------------------------------------------------
- local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_SVR_SESS)
- if ssl_ctx == nil then error("Error: Server context is invalid") end
- if private_key_file ~= nil then
- local obj_type = axtlsl.SSL_OBJ_RSA_KEY
- if string.find(private_key_file, ".p8") then
- obj_type = axtlsl.SSL_OBJ_PKCS8
- end
- if string.find(private_key_file, ".p12") then
- obj_type = axtlsl.SSL_OBJ_PKCS12
- end
- if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file,
- password) ~= axtlsl.SSL_OK then
- error("Private key '" .. private_key_file .. "' is undefined.")
- end
- end
- for _, v in ipairs(cert) do
- if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~=
- axtlsl.SSL_OK then
- error("Certificate '"..v .. "' is undefined.")
- end
- end
- for _, v in ipairs(ca_cert) do
- if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~=
- axtlsl.SSL_OK then
- error("Certificate '"..v .."' is undefined.")
- end
- end
- while true do
- if not quiet then print("ACCEPT") end
- local client_sock = server_sock:accept();
- local ssl = axtlsl.ssl_server_new(ssl_ctx, client_sock:getfd())
- -- do the actual SSL handshake
- local connected = false
- local res
- local buf
- while true do
- socket.select({client_sock}, nil)
- res, buf = axtlsl.ssl_read(ssl)
- if res == axtlsl.SSL_OK then -- connection established and ok
- if axtlsl.ssl_handshake_status(ssl) == axtlsl.SSL_OK then
- if not quiet and not connected then
- display_session_id(ssl)
- display_cipher(ssl)
- end
- connected = true
- end
- end
- if res > axtlsl.SSL_OK then
- for _, v in ipairs(buf) do
- io.write(string.format("%c", v))
- end
- elseif res < axtlsl.SSL_OK then
- if not quiet then
- axtlsl.ssl_display_error(res)
- end
- break
- end
- end
- -- client was disconnected or the handshake failed.
- print("CONNECTION CLOSED")
- axtlsl.ssl_free(ssl)
- client_sock:close()
- end
- axtlsl.ssl_ctx_free(ssl_ctx)
- end
- --
- -- Implement the SSL client logic.
- --
- function do_client(build_mode)
- local i = 2
- local v
- local port = 4433
- local options =
- bit.bor(axtlsl.SSL_SERVER_VERIFY_LATER, axtlsl.SSL_DISPLAY_CERTS)
- local private_key_file = nil
- local reconnect = 0
- local quiet = false
- local password = ""
- local session_id = {}
- local host = "127.0.0.1"
- local cert_size = axtlsl.ssl_get_config(axtlsl.SSL_MAX_CERT_CFG_OFFSET)
- local ca_cert_size = axtlsl.
- ssl_get_config(axtlsl.SSL_MAX_CA_CERT_CFG_OFFSET)
- local cert = {}
- local ca_cert = {}
- while i <= #arg do
- if arg[i] == "-connect" then
- if i >= #arg then
- print_client_options(build_mode, arg[i])
- end
- i = i + 1
- local t = string.find(arg[i], ":")
- host = string.sub(arg[i], 1, t-1)
- port = string.sub(arg[i], t+1)
- elseif arg[i] == "-cert" then
- if i >= #arg or #cert >= cert_size then
- print_client_options(build_mode, arg[i])
- end
- i = i + 1
- table.insert(cert, arg[i])
- elseif arg[i] == "-key" then
- if i >= #arg then
- print_client_options(build_mode, arg[i])
- end
- i = i + 1
- private_key_file = arg[i]
- options = bit.bor(options, axtlsl.SSL_NO_DEFAULT_KEY)
- elseif arg[i] == "-CAfile" then
- if i >= #arg or #ca_cert >= ca_cert_size then
- print_client_options(build_mode, arg[i])
- end
- i = i + 1
- table.insert(ca_cert, arg[i])
- elseif arg[i] == "-verify" then
- options = bit.band(options,
- bit.bnot(axtlsl.SSL_SERVER_VERIFY_LATER))
- elseif arg[i] == "-reconnect" then
- reconnect = 4
- elseif arg[i] == "-quiet" then
- quiet = true
- options = bit.band(options, bnot(axtlsl.SSL_DISPLAY_CERTS))
- elseif arg[i] == "-pass" then
- if i >= #arg then
- print_server_options(build_mode, arg[i])
- end
- i = i + 1
- password = arg[i]
- elseif build_mode == axtlsl.SSL_BUILD_FULL_MODE then
- if arg[i] == "-debug" then
- options = bit.bor(options, axtlsl.SSL_DISPLAY_BYTES)
- elseif arg[i] == "-state" then
- options = bit.bor(axtlsl.SSL_DISPLAY_STATES)
- elseif arg[i] == "-show-rsa" then
- options = bit.bor(axtlsl.SSL_DISPLAY_RSA)
- else -- don't know what this is
- print_client_options(build_mode, arg[i])
- end
- else -- don't know what this is
- print_client_options(build_mode, arg[i])
- end
- i = i + 1
- end
- local client_sock = socket.try(socket.connect(host, port))
- local ssl
- local res
- if not quiet then print("CONNECTED") end
- ---------------------------------------------------------------------------
- -- This is where the interesting stuff happens. Up until now we've
- -- just been setting up sockets etc. Now we do the SSL handshake.
- ---------------------------------------------------------------------------
- local ssl_ctx = axtlsl.ssl_ctx_new(options, axtlsl.SSL_DEFAULT_CLNT_SESS)
- if ssl_ctx == nil then
- error("Error: Client context is invalid")
- end
- if private_key_file ~= nil then
- local obj_type = axtlsl.SSL_OBJ_RSA_KEY
- if string.find(private_key_file, ".p8") then
- obj_type = axtlsl.SSL_OBJ_PKCS8
- end
- if string.find(private_key_file, ".p12") then
- obj_type = axtlsl.SSL_OBJ_PKCS12
- end
- if axtlsl.ssl_obj_load(ssl_ctx, obj_type, private_key_file,
- password) ~= axtlsl.SSL_OK then
- error("Private key '"..private_key_file.."' is undefined.")
- end
- end
- for _, v in ipairs(cert) do
- if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CERT, v, "") ~=
- axtlsl.SSL_OK then
- error("Certificate '"..v .. "' is undefined.")
- end
- end
- for _, v in ipairs(ca_cert) do
- if axtlsl.ssl_obj_load(ssl_ctx, axtlsl.SSL_OBJ_X509_CACERT, v, "") ~=
- axtlsl.SSL_OK then
- error("Certificate '"..v .."' is undefined.")
- end
- end
- -- Try session resumption?
- if reconnect ~= 0 then
- local session_id = nil
- local sess_id_size = 0
- while reconnect > 0 do
- reconnect = reconnect - 1
- ssl = axtlsl.ssl_client_new(ssl_ctx,
- client_sock:getfd(), session_id, sess_id_size)
- res = axtlsl.ssl_handshake_status(ssl)
- if res ~= axtlsl.SSL_OK then
- if not quiet then axtlsl.ssl_display_error(res) end
- axtlsl.ssl_free(ssl)
- os.exit(1)
- end
- display_session_id(ssl)
- session_id = axtlsl.ssl_get_session_id(ssl)
- sess_id_size = axtlsl.ssl_get_session_id_size(ssl)
- if reconnect > 0 then
- axtlsl.ssl_free(ssl)
- client_sock:close()
- client_sock = socket.try(socket.connect(host, port))
- end
- end
- else
- ssl = axtlsl.ssl_client_new(ssl_ctx, client_sock:getfd(), nil, 0)
- end
- -- check the return status
- res = axtlsl.ssl_handshake_status(ssl)
- if res ~= axtlsl.SSL_OK then
- if not quiet then axtlsl.ssl_display_error(res) end
- os.exit(1)
- end
- if not quiet then
- local common_name = axtlsl.ssl_get_cert_dn(ssl,
- axtlsl.SSL_X509_CERT_COMMON_NAME)
- if common_name ~= nil then
- print("Common Name:\t\t\t"..common_name)
- end
- display_session_id(ssl)
- display_cipher(ssl)
- end
- while true do
- local line = io.read()
- if line == nil then break end
- local bytes = {}
- for i = 1, #line do
- bytes[i] = line.byte(line, i)
- end
- bytes[#line+1] = 10 -- add carriage return, null
- bytes[#line+2] = 0
- res = axtlsl.ssl_write(ssl, bytes, #bytes)
- if res < axtlsl.SSL_OK then
- if not quiet then axtlsl.ssl_display_error(res) end
- break
- end
- end
- axtlsl.ssl_ctx_free(ssl_ctx)
- client_sock:close()
- end
- --
- -- Display what cipher we are using
- --
- function display_cipher(ssl)
- io.write("CIPHER is ")
- local cipher_id = axtlsl.ssl_get_cipher_id(ssl)
- if cipher_id == axtlsl.SSL_AES128_SHA then
- print("AES128-SHA")
- elseif cipher_id == axtlsl.SSL_AES256_SHA then
- print("AES256-SHA")
- elseif axtlsl.SSL_RC4_128_SHA then
- print("RC4-SHA")
- elseif axtlsl.SSL_RC4_128_MD5 then
- print("RC4-MD5")
- else
- print("Unknown - "..cipher_id)
- end
- end
- --
- -- Display what session id we have.
- --
- function display_session_id(ssl)
- local session_id = axtlsl.ssl_get_session_id(ssl)
- local v
- if #session_id > 0 then
- print("-----BEGIN SSL SESSION PARAMETERS-----")
- for _, v in ipairs(session_id) do
- io.write(string.format("%02x", v))
- end
- print("\n-----END SSL SESSION PARAMETERS-----")
- end
- end
- --
- -- Main entry point. Doesn't do much except works out whether we are a client
- -- or a server.
- --
- if #arg == 0 or (arg[1] ~= "s_server" and arg[1] ~= "s_client") then
- print_options(#arg > 0 and arg[1] or "")
- end
- local build_mode = axtlsl.ssl_get_config(axtlsl.SSL_BUILD_MODE)
- _ = arg[1] == "s_server" and do_server(build_mode) or do_client(build_mode)
- os.exit(0)
|