Ssl

The ssl library implements (most of) the LuaSec API.

The most common use of this library is to wrap an existing Socket and use it as a client, connecting out to a remote server (for example, over HTTPS). ssl.wrap does not apply any defaults of its own – whatever SslConfig table you pass it is used as-is – so it is up to the caller to supply reasonable values. The fields below are the ones that matter for a client connection and the values you will typically want to use.

mode

Should always be set to "client" when connecting out to a server. ("server" is only relevant if this code is itself accepting inbound connections.)

protocol

The protocol version to negotiate. "any" lets the two sides negotiate the highest protocol version they both support, and is a reasonable default for a client connecting to an unknown server.

options

A list of protocol tweaks and bug workarounds to enable. For a client connecting to a modern server, {"all", "no_sslv2", "no_sslv3", "no_tlsv1"} is a reasonable set: "all" turns on the standard collection of SSL bug workarounds, and the remaining entries disable the old, insecure SSLv2, SSLv3, and TLSv1 protocols.

verify

Controls whether the server’s certificate is verified, and how. One of none, peer, fail_if_no_peer_cert, or client_once. When connecting as a client, "peer" should be preferred if a certificate is provided (against cafile/capath).

cafile / capath

The certificate authority root certificate (or a path to one), or a directory of trusted CA certificates, used to validate the server’s certificate when verify is not "none". Neither has a value unless one is explicitly provided.

certificates

A list of CertificateTable. This is only needed on the client side if the server requires mutual TLS (i.e. it also verifies the client’s certificate); most client connections to a server can leave this unset.

depth

The maximum length of the certificate chain to verify. If left unset, the underlying library’s own default depth is used.

Driver Example

Looking at the SmartThings JBL driver, we can see a basic example of establishing an SSL connection.

First, a selfSignedRoot.crt file is provided at the base of the directory. A default SSL_CONFIG is then defined in drivers/SmartThings/jbl/src/jbl/api.lua. This configuration can be passed to the SSL wrapper when establishing a socket connection.

Error handling should be included in a production implementation, but has been omitted from this example for brevity.

local SSL_CONFIG = {
    mode = "client",
    protocol = "any",
    verify = "peer",
    options = "all",
    cafile = "./selfSignedRoot.crt"
}

local sock, _ = socket.tcp()
_, err = sock:connect(host, port)
sock, err = ssl.wrap(sock, SSL_CONFIG)
sock:dohandshake()

sock:send("GET /get_info HTTP/1.1\r\nHost: " .. host .. "\r\n\r\n")
local response, err = sock:receive("*a")
ssl.wrap(sock, config)

Wrap a socket with a TLS Stream

Parameters:
class ssl.CertificateTable

Both the certificate and key field can either be a relative path pointing to a file in the driver package or the contents as a string formatted in the PEM format.

certificate: str

The certificate

key: str

Optional private key, should only be set in 1 of the certificates list

password: str

Optional password

class ssl.SslConfig
mode: str

client or server

protocol: str

any

cafile: str

The contents of the certificate authority’s root cert or relative path to the root cert file

capath: str

A path to the certificate authority directory

certificates: list[CertificateTable]

list of certificates

verify: str

One of none, peer, fail_if_no_peer_cert, or client_once

depth: integer

The depth to verify

options: list[str]

“all” | “allow_unsafe_legacy_renegotiation” | “cipher_server_preference” | “cookie_exchange” | “dont_insert_empty_fragments” | “no_compression” | “no_dtlsv1” | “no_dtlsv1_2” | “no_query_mtu” | “no_session_resumption_on_renegotiation” | “no_ssl_mask” | “no_sslv2” | “no_sslv3” | “no_ticket” | “no_tlsv1” | “no_tlsv1_1” | “no_tlsv1_2” | “single_dh_use” | “single_ecdh_use” | “tls_rollback_bug”