tlslp.client

Open a TLS client that responds to server messages.

A set of default valid messages and responses are included.

The client connects to a server, receives newline-delimited commands, and responds one-by-one. The handshake consists of HELLO followed by a WORK challenge. The WORK challenge must be resolved within 30 minutes with n_bits=36; it is solved by invoking a compiled C++ helper (work_challenge).

Main functions:
prepare_client_socket:

Create a TLS-wrapped socket (secure or insecure).

hasher:

Compute SHA256(token + payload) as a hex string.

decipher_message:

Parse and validate a received command line.

handle_work_cpp:

Run the WORK helper and parse its RESULT:<suffix> output line.

define_response:

Create the protocol response for a single server command.

main:

CLI entry point.

Functions

args_to_client_config(ns)

Convert parsed CLI args into a ClientConfig.

build_client_parser()

Build the argparse parser for the tlslp-client CLI.

connect_to_server(sock, server_host, port)

Connect to server and return True if connection was successful.

decipher_message(message, valid_messages)

Parse and validate a server message.

define_response(args, token, valid_messages, ...)

Create a response for a single server command and enqueue it.

handle_work_cpp(token, n_bits[, bin_path, ...])

Solve the WORK challenge using the external C++ helper.

hasher(token, input_string)

Hash a string using SHA256.

main([argv])

Entry point for the CLI.

prepare_client_socket(ca_cert_path, ...[, ...])

Create a TLS-wrapped client socket.

run_work_binary(bin_path, token, n_bits[, ...])

Run the WORK challenge C++ binary.

Classes

ClientConfig(server_host, ports, ...)

Configuration for the TLS client CLI.

class tlslp.client.ClientConfig(server_host: str, ports: list[int], client_cert: str, private_key: str, ca_cert: str, work_binary: str, work_timeout: int, other_timeout: int, insecure: bool, log_level: str, tutorial: bool)

Bases: object

Configuration for the TLS client CLI.

This is built from CLI arguments and passed through to connection and message handling helpers.

ca_cert: str
client_cert: str
insecure: bool
log_level: str
other_timeout: int
ports: list[int]
private_key: str
server_host: str
tutorial: bool
work_binary: str
work_timeout: int
tlslp.client.args_to_client_config(ns: Namespace) ClientConfig

Convert parsed CLI args into a ClientConfig.

Parameters:

ns (argparse.Namespace) – Parsed CLI args.

Returns:

Normalized configuration.

Return type:

ClientConfig

tlslp.client.build_client_parser() ArgumentParser

Build the argparse parser for the tlslp-client CLI.

Returns:

Configured parser.

Return type:

argparse.ArgumentParser

tlslp.client.connect_to_server(sock: socket, server_host: str, port: int) bool

Connect to server and return True if connection was successful.

Parameters:
  • sock (socket.socket) – The socket to connect to.

  • server_host (str) – The server_host to connect to.

  • port (int) – The port to connect to.

Returns:

True if connection was successful, False otherwise.

Return type:

bool

tlslp.client.decipher_message(message: str, valid_messages: set[str]) list[str]

Parse and validate a server message.

Splits the message into whitespace-delimited tokens and validates that the first token is a known command. If the server sends only a command (no argument), an empty second token is appended so callers can uniformly access args[1].

Parameters:
  • message (str) – The received message line (with or without trailing newline).

  • valid_messages (set[str]) – Allowed command names.

Returns:

Parsed tokens, with at least two elements.

Return type:

list[str]

Raises:

ValueError – If the message is empty or the command is not in valid_messages.

Examples

>>> from tlslp.client import decipher_message
>>> vm = {"EMAIL1", "HELLO", "WORK", "DONE", "FAIL"}
>>> decipher_message("EMAIL1 LGTk\n", vm)
['EMAIL1', 'LGTk']
tlslp.client.define_response(args: list[str], token: str, valid_messages: set[str], queue: Queue, responses: dict[str, str] = {'ADDR_LINE1': '234 Evergreen Terrace', 'ADDR_LINE2': 'Springfield', 'BIRTHDATE': '99.99.1982', 'COUNTRY': 'USA', 'EMAIL1': 'elliottbache@gmail.com', 'EMAIL2': 'elliottbache2@gmail.com', 'FULL_NAME': 'Elliott Bache', 'SOCIAL': 'elliottbache@hotmail.com'}, bin_path: Path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/tls-line-protocol/checkouts/latest/src/tlslp/_bin/work_challenge')) None

Create a response for a single server command and enqueue it.

This computes the appropriate response for a parsed server message and places a tuple (is_err, result) into queue.

Parameters:
  • args (list[str]) – Parsed server tokens (command is args[0]).

  • token (str) – Current authentication data (set after receiving WORK).

  • valid_messages (set[str]) – Allowed command names.

  • queue (multiprocessing.Queue) – Queue-like object that supports put(...).

  • responses (dict[str, str], optional) – Static responses for body commands.

  • bin_path (Path, optional) – Path to the WORK solver binary.

Returns:

None

Examples

>>> import tlslp.client as c
>>> class Q:
...     def __init__(self): self.items = []
...     def put(self, x): self.items.append(x)
>>> q = Q()
>>> c.define_response(["HELLO"], token="", valid_messages={"HELLO"}, queue=q, responses={})
>>> q.items
[(False, 'HELLOBACK\n')]
tlslp.client.handle_work_cpp(token: str, n_bits: str, bin_path: Path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/tls-line-protocol/checkouts/latest/src/tlslp/_bin/work_challenge'), timeout: int = 1800) str

Solve the WORK challenge using the external C++ helper.

Runs the WORK binary and parses the first stdout line starting with RESULT:. The returned suffix includes a trailing newline to match the wire protocol.

Parameters:
  • token (str) – Authentication data from the server.

  • n_bits (str) – Required number of trailing zero bits

  • bin_path (Path, optional) – Path to the WORK solver binary.

  • timeout (int, optional) – Subprocess timeout in seconds.

Returns:

The suffix followed by "\n".

Return type:

str

Raises:
  • ValueError – If no RESULT: line is found or the suffix is empty.

  • subprocess.CalledProcessError – If the solver exits non-zero.

Examples

>>> from pathlib import Path
>>> import tlslp.client as c
>>> class R:  # fake CompletedProcess
...     stdout = "RESULT:abcd\n"
>>> c.run_work_binary = lambda *a, **k: R()
>>> c.handle_work_cpp("AUTH", "4", bin_path=Path("work_challenge"))
'abcd\n'
tlslp.client.hasher(token: str, input_string: str) str

Hash a string using SHA256.

Concatenates token and input_string and returns the SHA256 digest in lowercase hex.

Parameters:
  • token (str) – Authentication data provided by the server.

  • input_string (str) – ASCII payload provided by the server.

Returns:

SHA256(token + input_string) as a hex string.

Return type:

str

Examples

>>> from tlslp.client import hasher
>>> token = "AUTH"
>>> hasher(auth, "LGTk")
'189af41571a36ba3655451530a84e33f018bdca2'
tlslp.client.main(argv: Sequence[str] | None = None) int

Entry point for the CLI.

Parameters:

argv – sys.argv[1:] is used.

Returns:

0 on success; nonzero on error.

Return type:

Process exit code

Side effects:

Opens network connections, prints to stdout/stderr.

tlslp.client.prepare_client_socket(ca_cert_path: str, client_cert_path: str, private_key_path: str, server_host: str, is_secure: bool = False) socket

Create a TLS-wrapped client socket.

In secure mode (is_secure=True), the client verifies the server certificate against ca_cert_path and enables hostname verification.

In insecure mode (is_secure=False), certificate verification is disabled and hostname checks are disabled; to avoid accidental misuse, insecure mode is only allowed for localhost.

The returned socket has a default timeout of min(DEFAULT_OTHER_TIMEOUT, DEFAULT_WORK_TIMEOUT); callers may override this via settimeout(...).

Parameters:
  • ca_cert_path (str) – Path to CA certificate (PEM).

  • client_cert_path (str) – Path to client certificate (PEM).

  • private_key_path (str) – Path to client private key (PEM).

  • server_host (str) – Server hostname.

  • is_secure (bool, optional) – If True, verify the server certificate and hostname.

Returns:

A TLS-wrapped socket ready to connect(...).

Return type:

socket.socket

Raises:

ValueError – If insecure mode is requested for a non-local host.

tlslp.client.run_work_binary(bin_path: Path, token: str, n_bits: str, timeout: int = 1800) CompletedProcess

Run the WORK challenge C++ binary.

Parameters:
  • bin_path (Path) – The path of the C++ binary.

  • token (str) – The authentication data to use.

  • n_bits (str) – The n_bits to use.

  • timeout (int, optional) – The timeout to use.

Returns:

The completed process.

Return type:

subprocess.CompletedProcess

Notes

In subprocess.run,
  • the environment variables are scrubbed, leaving only the simplest (env={“LC_ALL”: “C”})

  • the stdout and stderr are returned as text and not bytes (text=True, capture_output=True)

  • the exit status is returned and a CalledProcessError exception is raised if non-zero (check=True)

  • the timeout is set at 30 minutes (timeout=timeout)

  • the current working directory is set as the binary’s directory to avoid flakiness in tests (cwd=str(cpp_binary_path.parent))