Skip to main content

Python API Reference

Overview​

The Python entity-side API supports two application workflows:

  1. Create a secure channel with SecureClient, SecureServer, and SecureChannel. This is the recommended workflow for most applications.
  2. Manage session keys explicitly with IoTAuthContext.request_session_keys() and SessionKeyCache when an application needs to select or reuse keys.

Most applications only need these top-level imports:

from iotauth import IoTAuthContext, SecureClient, SecureServer, SecureChannel, IoTAuthError

The iotauth package root intentionally exposes only the application-facing client, server, channel, context, configuration and key models, and public exceptions. Low-level protocol, cryptography, handshake, serialization, transport, and credential helpers remain available from their dedicated modules and are not top-level imports. For example:

from iotauth.protocol import IoTSPFrame, MessageType, parse_frame
from iotauth.crypto import symmetric_encrypt_authenticate
from iotauth.transports import recv_frame, send_frame

Applications should normally use SecureClient, SecureServer, and SecureChannel instead of calling those low-level helpers directly.

Common setup: IoTAuthContext​

The IoTAuthContext class holds the runtime state of a Python entity: configuration parameters, cryptographic keys, distribution-key settings, and cached session keys.

Constructors​

IoTAuthContext.from_config​

@classmethod
def from_config(cls, path: str | Path, *, validate_paths: bool = True) -> IoTAuthContext

Reads an entity configuration file, loads its configured credentials or permanent distribution key, and initializes an IoTAuthContext. This is the only public configuration entry point and is the method applications should use.

The internal parser supports both C-style dotted properties (key=value) and Node-style JSON configuration files. It detects JSON from the first non-whitespace character ({), rather than relying on the filename extension. Both formats produce the same runtime context.

Relative credential and key paths are resolved differently by format:

  • Properties paths are relative to the directory containing the config file.

  • JSON paths are relative to the current working directory from which the Python process is started.

  • Absolute paths work in both formats.

  • Parameters:

    • path: Path to a properties or JSON configuration file, commonly using the .config extension.
    • validate_paths: If True, verifies that configured credential and key files exist during initialization.
  • Returns: An initialized IoTAuthContext instance with an empty SessionKeyCache.

  • Raises: ConfigError or CredentialError if the configuration is invalid or files cannot be loaded.

The parsed configuration is available as ctx.config for inspection. Applications should treat it as file-owned runtime configuration and should not modify it in code. To change configuration, update the source file and create a new context with from_config().


1. Create a secure channel​

Use this workflow when an application only needs to establish an encrypted connection and exchange messages. SecureClient and SecureServer handle key acquisition and the secure handshake. Application data is always sent through the returned SecureChannel.

SecureClient​

A wrapper for client-side key acquisition, connection establishment, and the secure handshake.

Constructor​

def __init__(
self,
ctx: IoTAuthContext,
*,
key: SessionKey | None = None,
purpose: dict[str, object] | str | None = None,
host: str | None = None,
port: int | None = None,
timeout: float | None = 5.0,
)
  • ctx: The runtime IoTAuthContext.
  • key: Optional session key. If omitted, connect() requests one from Auth.
  • purpose: Optional purpose override used when requesting a key.
  • host / port: Optional peer address. If omitted, the first configured target is used.
  • timeout: Timeout for Auth, connection, and handshake operations.

connect​

def connect(self) -> SecureChannel

Returns an open SecureChannel. If the client has no selected key, this method requests keys from Auth and selects the first before connecting to the peer. It retains that key for later connections; it does not automatically select a key from the context cache or refresh an expired key. Pass key= to reuse a specific cached key.

Each SecureClient owns at most one open channel. Close the returned channel before calling connect() again; otherwise connect() raises SecureClientStateError.

Client example​

ctx = IoTAuthContext.from_config("path/to/client.config")

with SecureClient(ctx) as client:
channel = client.connect()
channel.send(b"hello")
reply = channel.recv(timeout=5.0)

Exiting the with block closes the active channel. To close it earlier, call channel.close().


SecureServer​

A wrapper that listens for incoming TCP connections from peer entities and completes secure handshakes.

Constructor​

def __init__(
self,
ctx: IoTAuthContext,
*,
host: str | None = None,
port: int | None = None,
backlog: int = 5,
accept_timeout: float | None = None,
handshake_timeout: float | None = 5.0,
)
  • ctx: The runtime IoTAuthContext.
  • host / port: Listen address. If omitted, the first configured target is used.
  • backlog: Maximum pending TCP connections passed to socket.listen().
  • accept_timeout: Maximum seconds to wait for an incoming TCP connection. The default, None, waits indefinitely.
  • handshake_timeout: Maximum seconds to complete the secure handshake after accepting a peer. The default is five seconds. Use None to wait indefinitely.

serve_once​

def serve_once(self) -> SecureChannel

Blocks until a single client TCP connection is accepted, reads the session key ID from the initial handshake frame, fetches the key if necessary, completes the challenge/response verification, and returns a connected SecureChannel.

Server example​

ctx = IoTAuthContext.from_config("path/to/server.config")

with SecureServer(ctx) as server:
channel = server.serve_once()
try:
message = channel.recv()
channel.send(b"ack")
finally:
channel.close()

Exiting the with block closes the server's listening socket. The application closes each channel returned by serve_once().


SecureChannel​

Represents an established, bidirectional encrypted communication session between two entities. The socket, session key, and send/receive sequence counters are managed internally. Applications can inspect the read-only closed property and use the methods below.

Methods​

send​

def send(self, data: bytes) -> None

Encrypts data using the active session key and cryptographic mode (for example, AES_128_CBC or AES_128_GCM) and transmits the framed bytes over the TCP socket.

recv​

def recv(self, *, timeout: float | None = None) -> bytes

Reads an incoming frame from the TCP socket, verifies any HMAC/GCM authentication tags, decrypts the payload using the session key, and returns the raw plaintext bytes. timeout limits how many seconds the operation waits for a complete message; None waits indefinitely. The socket's previous timeout is restored after the call succeeds or fails.

close​

def close(self) -> None

Closes the underlying TCP connection.


2. Manage session keys explicitly​

Use this workflow when an application needs to request several keys, inspect the key cache, or choose a specific key before creating a secure channel.

IoTAuthContext.request_session_keys​

def request_session_keys(
self,
*,
purpose: dict[str, object] | str | None = None,
count: int | None = None,
timeout: float | None = 5.0,
) -> list[SessionKey]
  • purpose: Optional purpose override. If omitted, the configured purpose is used.
  • count: Number of keys to request. If omitted, the configured key count is used.
  • timeout: Socket timeout in seconds for communication with Auth.
  • Returns: The keys returned by Auth. They are also added to ctx.session_keys.
  • Raises: AuthConnectionError when Auth is unreachable and AuthProtocolError when Auth returns an unexpected response.

Request and select a key​

ctx = IoTAuthContext.from_config("path/to/client.config")

keys = ctx.request_session_keys(
purpose={"group": "Servers"},
count=2,
)

with SecureClient(ctx, key=keys[0]) as client:
channel = client.connect()
channel.send(b"hello")
reply = channel.recv()

Passing key= prevents SecureClient.connect() from requesting another key.

SessionKeyCache​

The context exposes its in-memory cache as ctx.session_keys:

ctx.session_keys.add(key, replace=False)
key = ctx.session_keys.get(key_id)
key = ctx.session_keys.require(key_id)
keys = ctx.session_keys.values()
  • add() stores a key. Set replace=True to replace a key with the same ID.
  • get() returns None when the key is absent.
  • require() raises KeyCacheError when the key is absent.
  • values() returns all cached keys.

SessionKey​

SessionKey(
id: bytes,
cipher_key: bytes,
mac_key: bytes | None,
abs_validity: int | None,
rel_validity: int | None,
encryption_mode: str,
hmac_enabled: bool,
permanent_distribution_key: bool,
first_use_ms: int | None = None,
)
  • id: Eight-byte session-key identifier.
  • cipher_key: Symmetric encryption key.
  • mac_key: Message-authentication key, or None when HMAC is disabled.
  • abs_validity: Absolute expiration time in epoch milliseconds.
  • rel_validity: Validity duration in milliseconds after first use.
  • encryption_mode: Configured symmetric encryption mode.
  • hmac_enabled: Whether messages require HMAC authentication.
  • permanent_distribution_key: Whether Auth issued the session key using permanent distribution-key mode.
  • first_use_ms: First-use time in epoch milliseconds used to evaluate relative validity. It is None until the key is first used.

Most applications should use keys returned by request_session_keys() rather than constructing them directly. Cipher and MAC key material is excluded from the object's representation to avoid exposing secrets in logs and tracebacks.


Buffer cryptography​

The iotauth.crypto module exposes standalone symmetric_encrypt_authenticate() and symmetric_decrypt_authenticate() functions for byte buffers, with AES-128-CBC, AES-128-CTR, and AES-128-GCM support. They take cipher/MAC keys and mode settings explicitly. There is no Crypto class or encrypt_payload() method in the package.

These low-level functions do not manage session-key validity, message framing, or sequence numbers. Use SecureChannel.send() and recv() for communication with peers.

Exceptions​

The API-specific exceptions below inherit from IoTAuthError (iotauth.exceptions). Invalid arguments can also raise standard Python exceptions; for example, recv(timeout=-1) raises ValueError.

ExceptionDescription
IoTAuthErrorBase class for all IoTAuth Python API errors.
ConfigErrorRaised when an .config file is missing required fields or malformed.
CredentialErrorRaised when a private key or certificate cannot be read or verified.
KeyCacheErrorRaised when a session-key cache operation is invalid.
SerializationErrorRaised when protocol bytes cannot be encoded or decoded.
AuthConnectionErrorRaised when TCP communication with Auth or a peer fails.
AuthProtocolErrorRaised when Auth returns a semantically unexpected message.
UnsupportedCryptoErrorRaised when a configured crypto operation is unsupported.
MessageIntegrityErrorRaised when signature, MAC, or authenticated decryption verification fails.
SecureHandshakeErrorRaised when the peer-to-peer secure handshake fails.
ExpiredKeyErrorRaised when an expired session key is used.
SecureChannelClosedRaised when an operation is attempted on a closed channel or the peer closes during receive.
SecureClientStateErrorRaised when a client already owns an open channel.
InvalidSequenceNumberErrorRaised when secure-message sequence validation fails.