From 538085169aba6fa9cbf737fae10f512790bdd62b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 5 Dec 2021 16:46:52 +0100 Subject: [PATCH] ADDED: library(tls), providing all predicates for TLS connections. The currently available predicates for TLS-connections can be extended with predicates to load and reason about certificates etc. --- README.md | 2 + src/lib/tls.pl | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/lib/tls.pl diff --git a/README.md b/README.md index b22a00d8..09878933 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,8 @@ The modules that ship with Scryer Prolog are also called ECDH key exchange over Curve25519 (X25519), authenticated symmetric encryption with ChaCha20-Poly1305, and reasoning about elliptic curves. * [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation +* [`tls`](src/lib/tls.pl) + Predicates for negotiating TLS connections explicitly. To use predicates provided by the `lists` library, write: diff --git a/src/lib/tls.pl b/src/lib/tls.pl new file mode 100644 index 00000000..3fae7aeb --- /dev/null +++ b/src/lib/tls.pl @@ -0,0 +1,110 @@ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Negotiation of TLS connections. + Written Dec. 2021 by Markus Triska (triska@metalevel.at) + Part of Scryer Prolog. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +:- module(tls, [tls_client_context/2, % -Context, +Options + tls_client_negotiate/3, % +Context, +Stream0, -Stream + tls_server_context/2, % -Context, +Options + tls_server_negotiate/3 % +Context, +Stream0, -Stream + ]). + +:- use_module(library(lists)). +:- use_module(library(error)). + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + TLS Clients + =========== + + Use tls_client_context/2 to create a TLS context, for example with: + + tls_client_context(Context, [hostname("metalevel.at")]) + + Using the context and an existing stream S0 (for example, the + result of socket_client_open/3), a TLS stream S can be negotiated + with: + + tls_client_negotiate(Context, S0, S) + + S will be an encrypted and authenticated stream with the server. + + The advantage of separating the creation of the client context from + negotiating a connection is that the context can be created only once, + and quickly reused if needed. This is currently not implemented: In + the present implementation, a new internal "Connector" is created for + every connection, using the specified hostname. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +tls_client_context(tls_context(Host), Options) :- + must_be(list, Options), + ( member(hostname(Host), Options) -> + must_be(chars, Host) + ; Host = "" + ). + +tls_client_negotiate(tls_context(Host), S0, S) :- + '$tls_client_connect'(Host, S0, S). + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + TLS Servers + =========== + + Use tls_server_context/2 to create a TLS context, for example with: + + tls_server_context(Context, [pkcs12(Chars)]) + + where Chars is a list of characters with the contents of a + DER-formatted PKCS #12 archive. The option password(Ps) can be used + to specify the password Ps (also a string) for decrypting the key. + On some versions of OSX, and potentially also on other platforms, + empty passwords are not supported. + + The archive should contain a leaf certificate and its private key, + as well any intermediate certificates that should be sent to + clients to allow them to build a chain to a trusted root. The chain + certificates should be in order from the leaf certificate towards + the root. + + PKCS #12 archives typically have the file extension .p12 or .pfx, + and can be created with the OpenSSL pkcs12 tool: + + $ openssl pkcs12 -export -out identity.pfx \ + -inkey key.pem -in cert.pem -certfile chain_certs.pem + + + You can use phrase_from_file/3 from library(pio) and seq//1 from + library(dcgs) to read the contents of "identity.pfx" into a string: + + phrase_from_file(seq(Chars), "identity.pfx", [type(binary)]) + + The obtained context should be treated as an opaque Prolog term. + + Using the context and an existing stream S0 (for example, the + result of socket_server_accept/4), a TLS stream S can be negotiated + by a Prolog-based server with: + + tls_server_negotiate(Context, S0, S) + + S will be an encrypted and authenticated stream with the client. + + The advantage of separating the creation of the server context from + negotiating a connection is that the context can be created only + once, and quickly cloned for every incoming connection. This is + currently not implemented: In the present implementation, a new context + is created for every connection, using the specified parameters. +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +tls_server_context(tls_context(Cert,Password), Options) :- + ( member(pcks12(Cert), Options) -> + must_be(chars, Cert) + ; domain_error(contains_pcks12, Options, tls_server_context/2) + ), + ( member(password(Password), Options) -> + must_be(chars, Password) + ; Password = "" + ). + +tls_server_negotiate(tls_context(Cert,Password), S0, S) :- + '$tls_accept_client'(Cert, Password, S0, S). + -- 2.54.0