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 was not constructed with a key, this method requests one from Auth before connecting to the peer.

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, payload: bytes) -> None

Encrypts payload 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.


Exceptions

All exceptions raised by the Python SDK inherit from IoTAuthError (iotauth.exceptions):

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.
SecureClientStateErrorRaised when a client already owns an open channel.
InvalidSequenceNumberErrorRaised when secure-message sequence validation fails.