Skip to content

derivepassphrase.ssh_agent

A bare-bones SSH agent client supporting signing and key listing.

TrailingDataError

TrailingDataError(
    raw: bytes | bytearray = b"",
    trailing: bytes | bytearray = b"",
)

Bases: RuntimeError

The result contained trailing data.

__repr__

__repr__() -> str

SSHAgentFailedError

Bases: RuntimeError

The SSH agent failed to complete the requested operation.

__repr__

__repr__() -> str

SSHAgentClient

SSHAgentClient(
    *, socket: SSHAgentSocket | Sequence[str] | None = None
)

A bare-bones SSH agent client supporting signing and key listing.

The main use case is requesting the agent sign some data, after checking that the necessary key is already loaded.

The main fleshed out methods are list_keys and sign, which implement the REQUEST_IDENTITIES and SIGN_REQUEST requests. If you really wanted to, there is enough infrastructure in place to issue other requests as defined in the protocol—it’s merely the wrapper functions and the protocol numbers table that are missing.

Parameters:

Name Type Description Default
socket SSHAgentSocket | Sequence[str] | None

An optional socket-like object, already connected to the SSH agent, or a list of names of socket providers to try. If not given, we query platform-specific default addresses, if possible.

None

Raises:

Type Description
NoSuchProviderError

The list of socket provider names contained an invalid entry.

KeyError

An expected configuration entry or an expected environment variable is missing.

NotImplementedError

The named socket provider is not functional or not applicable to this derivepassphrase installation, e.g. because it is generally not implemented yet, or it requires a specific operating system, or specific system functionality that is not provided by all Python versions, or external packages that are unavailable on this installation.

This error may be raised multiple times, as an exception group.

OSError

There was an error setting up a socket connection to the agent.

SOCKET_PROVIDERS class-attribute instance-attribute

SOCKET_PROVIDERS: ClassVar = ('native',)

The default list of SSH agent socket providers.

__enter__

__enter__() -> Self

Close socket connection upon context manager completion.

Returns:

Type Description
Self

Self.

__exit__

__exit__(
    exc_type: type[BaseException] | None,
    exc_val: BaseException | None,
    exc_tb: TracebackType | None,
) -> bool

Close socket connection upon context manager completion.

Parameters:

Name Type Description Default
exc_type type[BaseException] | None

An optional exception type.

required
exc_val BaseException | None

An optional exception value.

required
exc_tb TracebackType | None

An optional exception traceback.

required

Returns:

Type Description
bool

True if the exception was handled, false if it should propagate.

uint32 staticmethod

uint32(num: int) -> bytes

Format the number as a uint32, as per the agent protocol.

Parameters:

Name Type Description Default
num int

A number.

required

Returns:

Type Description
bytes

The number in SSH agent wire protocol format, i.e. as a 32-bit big endian number.

Raises:

Type Description
OverflowError

As per int.to_bytes.

Examples:

>>> SSHAgentClient.uint32(16777216)
b'\x01\x00\x00\x00'

string classmethod

string(payload: Buffer) -> bytes

Format the payload as an SSH string, as per the agent protocol.

Parameters:

Name Type Description Default
payload Buffer

A bytes-like object.

required

Returns:

Type Description
bytes

The payload, framed in the SSH agent wire protocol format, as a bytes object.

Examples:

>>> SSHAgentClient.string(b"ssh-rsa")
b'\x00\x00\x00\x07ssh-rsa'

unstring classmethod

unstring(bytestring: Buffer) -> bytes

Unpack an SSH string.

Parameters:

Name Type Description Default
bytestring Buffer

A framed bytes-like object.

required

Returns:

Type Description
bytes

The payload, as a bytes object.

Raises:

Type Description
ValueError

The byte string is not an SSH string.

Examples:

>>> SSHAgentClient.unstring(b"\x00\x00\x00\x07ssh-rsa")
b'ssh-rsa'
>>> SSHAgentClient.unstring(SSHAgentClient.string(b"ssh-ed25519"))
b'ssh-ed25519'

unstring_prefix classmethod

unstring_prefix(bytestring: Buffer) -> tuple[bytes, bytes]

Unpack an SSH string at the beginning of the byte string.

Parameters:

Name Type Description Default
bytestring Buffer

A bytes-like object, beginning with a framed/SSH byte string.

required

Returns:

Type Description
tuple[bytes, bytes]

A 2-tuple (a, b), where a is the unframed byte string/payload at the beginning of input byte string, and b is the remainder of the input byte string.

Raises:

Type Description
ValueError

The byte string does not begin with an SSH string.

Examples:

>>> SSHAgentClient.unstring_prefix(
...     b"\x00\x00\x00\x07ssh-rsa____trailing data"
... )
(b'ssh-rsa', b'____trailing data')
>>> SSHAgentClient.unstring_prefix(
...     SSHAgentClient.string(b"ssh-ed25519")
... )
(b'ssh-ed25519', b'')

ensure_agent_subcontext classmethod

ensure_agent_subcontext(
    conn: (
        SSHAgentClient
        | SSHAgentSocket
        | Sequence[str]
        | None
    ) = None,
) -> Iterator[SSHAgentClient]

Return an SSH agent client subcontext.

If necessary, construct an SSH agent client first using the connection hint.

Parameters:

Name Type Description Default
conn SSHAgentClient | SSHAgentSocket | Sequence[str] | None

If an existing SSH agent client, then enter a context within this client’s scope. After exiting the context, the client persists, including its socket.

If a socket, then construct a client using this socket, then enter a context within this client’s scope. After exiting the context, the client is destroyed and the socket is closed.

If None, or a list of names of socket providers, then construct a client according to those connection hints (“auto-discovery”), and enter a context within this client’s scope. After exiting the context, both the client and its socket are destroyed.

None

Yields:

Type Description
SSHAgentClient

When entering this context, return the SSH agent client.

Raises:

Type Description
NoSuchProviderError

As per __init__.

KeyError

As per __init__, including the multiple raise as an exception group.

NotImplementedError

As per __init__.

OSError

As per __init__.

has_deterministic_dsa_signatures

has_deterministic_dsa_signatures() -> bool

Check whether the agent returns deterministic DSA signatures.

This includes ECDSA signatures.

Generally, this means that the SSH agent implements RFC 6979 or a similar system.

Returns:

Type Description
bool

True if a known agent was detected where signatures are deterministic for all DSA key types, false otherwise.

Known agents with deterministic signatures
agent detected via
Pageant (PuTTY) list-extended@putty.projects.tartarus.org extension request

request

request(
    code: int | SSH_AGENTC,
    payload: Buffer,
    /,
    *,
    response_code: None = None,
) -> tuple[int, bytes]
request(
    code: int | SSH_AGENTC,
    payload: Buffer,
    /,
    *,
    response_code: Iterable[SSH_AGENT | int] = frozenset(
        {SUCCESS}
    ),
) -> bytes
request(
    code: int | SSH_AGENTC,
    payload: Buffer,
    /,
    *,
    response_code: SSH_AGENT | int = SUCCESS,
) -> bytes
request(
    code: int | SSH_AGENTC,
    payload: Buffer,
    /,
    *,
    response_code: (
        Iterable[SSH_AGENT | int] | SSH_AGENT | int | None
    ) = None,
) -> tuple[int, bytes] | bytes

Issue a generic request to the SSH agent.

Parameters:

Name Type Description Default
code int | SSH_AGENTC

The request code. See the SSH agent protocol for protocol numbers to use here (and which protocol numbers to expect in a response).

required
payload Buffer

A bytes-like object containing the payload, or “contents”, of the request. Request-specific.

It is our responsibility to add any necessary wire framing around the request code and the payload, not the caller’s.

required
response_code Iterable[SSH_AGENT | int] | SSH_AGENT | int | None

An optional response code, or a set of response codes, that we expect. If given, and the actual response code does not match, raise an error.

None

Returns:

Type Description
tuple[int, bytes] | bytes

A 2-tuple consisting of the response code and the payload, with all wire framing removed.

If a response code was passed, then only return the payload.

Raises:

Type Description
EOFError

The response from the SSH agent is truncated or missing.

OSError

There was a communication error with the SSH agent.

SSHAgentFailedError

We expected specific response codes, but did not receive any of them.

list_keys

list_keys() -> Sequence[SSHKeyCommentPair]

Request a list of keys known to the SSH agent.

Returns:

Type Description
Sequence[SSHKeyCommentPair]

A read-only sequence of key/comment pairs.

Raises:

Type Description
EOFError

The response from the SSH agent is truncated or missing.

OSError

There was a communication error with the SSH agent.

TrailingDataError

The response from the SSH agent is too long.

SSHAgentFailedError

The agent failed to complete the request.

sign

sign(
    key: Buffer,
    payload: Buffer,
    *,
    flags: int = 0,
    check_if_key_loaded: bool = False
) -> bytes

Request the SSH agent sign the payload with the key.

Parameters:

Name Type Description Default
key Buffer

The public SSH key to sign the payload with, in the same format as returned by, e.g., the list_keys method. The corresponding private key must have previously been loaded into the agent to successfully issue a signature.

required
payload Buffer

A byte string of data to sign.

required
flags int

Optional flags for the signing request. Currently passed on as-is to the agent. In real-world usage, this could be used, e.g., to request more modern hash algorithms when signing with RSA keys. (No such real-world usage is currently implemented.)

0
check_if_key_loaded bool

If true, check beforehand (via list_keys) if the corresponding key has been loaded into the agent.

False

Returns:

Type Description
bytes

The binary signature of the payload under the given key.

Raises:

Type Description
EOFError

The response from the SSH agent is truncated or missing.

OSError

There was a communication error with the SSH agent.

TrailingDataError

The response from the SSH agent is too long.

SSHAgentFailedError

The agent failed to complete the request.

KeyError

check_if_key_loaded is true, and the key was not loaded into the agent.

query_extensions

query_extensions() -> Set[bytes]

Request a listing of extensions supported by the SSH agent.

Returns:

Type Description
Set[bytes]

A read-only set of extension names the SSH agent says it supports.

Raises:

Type Description
EOFError

The response from the SSH agent is truncated or missing.

OSError

There was a communication error with the SSH agent.

RuntimeError

The response from the SSH agent is malformed.

Note

The set of supported extensions is queried via the query extension request. If the agent does not support the query extension request, or extension requests in general, then an empty set is returned. This does not however imply that the agent doesn’t support any extension request… merely that it doesn’t support extension autodiscovery.

derivepassphrase.ssh_agent.socketprovider

Machinery for providing sockets connected to SSH agents.

SSHAgentSocketProviderT module-attribute

SSHAgentSocketProviderT = TypeVar(
    "SSHAgentSocketProviderT", bound=SSHAgentSocketProvider
)

A parametrized SSH agent socket provider.

OVERLAPPED

Bases: Structure

The data structure for overlapped (async) I/O on The Annoying OS.

NoSuchProviderError

Bases: KeyError

No such SSH agent socket provider is known.

UnixDomainSocketsNotAvailableError

Bases: NotImplementedError

This Python installation does not support UNIX domain sockets.

On this installation, the standard library symbol socket.AF_UNIX is not available, necessary to create UNIX domain sockets.

WindowsNamedPipesNotAvailableError

Bases: NotImplementedError

This Python installation does not support Windows named pipes.

On this installation, the standard library symbol ctypes.WinDLL is not available, necessary to interface with the Annoying Operating System’s standard library to create Windows named pipes.

WindowsNamedPipeHandle

WindowsNamedPipeHandle(name: str)

A Windows named pipe handle.

This handle implements the SSHAgentSocket interface. It is only constructable if the Python installation can successfully call into the Annoying OS kernel32.dll library via ctypes.

Parameters:

Name Type Description Default
name str

The named pipe’s name.

required

Raises:

Type Description
OSError

The system failed to open the named pipe.

ValueError

The named pipe’s name does not designate a named pipe.

WindowsNamedPipesNotAvailableError

This Python version does not support Windows named pipes.

named_pipe_name

named_pipe_name() -> str

Return the named pipe name this socket is connected to.

for_openssh classmethod

for_openssh() -> Self

Construct a named pipe for use with OpenSSH on The Annoying OS.

Returns:

Type Description
Self

A new named pipe handle, using OpenSSH’s pipe name.

for_pageant classmethod

for_pageant() -> Self

Construct a named pipe for use with Pageant.

Returns:

Type Description
Self

A new named pipe handle, using Pageant’s pipe name.

pageant_named_pipe_name staticmethod

pageant_named_pipe_name(
    *, require_cryptprotectmemory: bool = False
) -> str

Return the pipe name that Pageant on The Annoying OS would use.

Parameters:

Name Type Description Default
require_cryptprotectmemory bool

Pageant normally attempts to use the CryptProtectMemory system function from the crypt32.dll library on The Annoying OS, ignoring any errors. This in turn influences the resulting named pipe’s filename.

If true, and if we fail to call CryptProtectMemory, we abort; otherwise we ignore any errors from calling or failing to call CryptProtectMemory.

False

Raises:

Type Description
NotImplementedError

require_cryptprotectmemory was True, but this Python version cannot call CryptProtectMemory due to lack of system support.

SocketProvider

Static functionality for providing sockets.

registry class-attribute

registry: dict[str, SSHAgentSocketProvider | str | None] = (
    {}
)

A dictionary of callables that provide SSH agent sockets.

Each entry in the dictionary points either to a callable, a string, or None: if a callable, then that callable returns a socket; if a string, then this entry is an alias for that other entry; if None, then the entry name is merely registered, but no implementation is available.

If a callable is not applicable to this platform, Python installation or derivepassphrase installation, then it MUST raise NotImplementedError. Conversely, if the callable returns a value, or raises any other kind of exception, then the caller MAY assume that this platform, Python installation and derivepassphrase installation are sufficient for the callable to be able to return a working socket on this platform. (The latter may still be dependent on further, external circumstances, such as required configuration settings, or environment variables, or sufficient system resources, etc.)

(Interpretation of “MUST” and “MAY” as per IETF Best Current Practice #14; see RFC 2119 and RFC 8174.)

ENTRY_POINT_GROUP_NAME class-attribute instance-attribute

ENTRY_POINT_GROUP_NAME = (
    "derivepassphrase.ssh_agent_socket_providers"
)

The group name under which entry points for the SSH agent socket provider registry should be recorded. Each target of such an entry point should be a _types.SSHAgentSocketProviderEntry object.

unix_domain_ssh_auth_sock staticmethod

unix_domain_ssh_auth_sock(*, timeout: int = 125) -> socket

Return a UNIX domain socket connected to SSH_AUTH_SOCK.

Parameters:

Name Type Description Default
timeout int

A connection timeout for the SSH agent. Only used for “true” sockets, and only if the socket is not yet connected. The default value gives ample time for agent connections forwarded via SSH on high-latency networks (e.g. Tor).

125

Returns:

Type Description
socket

A connected UNIX domain socket.

Raises:

Type Description
KeyError

The SSH_AUTH_SOCK environment variable was not found.

UnixDomainSocketsNotAvailableError

This Python version does not support UNIX domain sockets, necessary to automatically connect to a running SSH agent via the SSH_AUTH_SOCK environment variable.

OSError

There was an error setting up a socket connection to the agent.

windows_named_pipe_for_pageant classmethod

windows_named_pipe_for_pageant() -> WindowsNamedPipeHandle

Return a socket wrapper connected to Pageant on The Annoying OS.

Raises:

Type Description
OSError

There was an error setting up a connection to Pageant.

WindowsNamedPipesNotAvailableError

This Python version does not support Windows named pipes.

windows_named_pipe_for_openssh classmethod

windows_named_pipe_for_openssh() -> WindowsNamedPipeHandle

Return a socket wrapper connected to the OpenSSH agent on The Annoying OS.

Raises:

Type Description
OSError

There was an error setting up a connection to the OpenSSH agent.

WindowsNamedPipesNotAvailableError

This Python version does not support Windows named pipes.

windows_named_pipe_ssh_auth_sock classmethod

windows_named_pipe_ssh_auth_sock() -> (
    WindowsNamedPipeHandle
)

Return a socket wrapper connected to the agent in SSH_AUTH_SOCK.

The SSH_AUTH_SOCK environment variable is assumed to contain a valid named pipe name, i.e., a path starting with \\.\pipe\ or //./pipe/.

Raises:

Type Description
KeyError

The SSH_AUTH_SOCK environment variable was not found.

ValueError

The path in SSH_AUTH_SOCK clearly does not name a valid named pipe name.

OSError

There was an error setting up a connection to the OpenSSH agent.

WindowsNamedPipesNotAvailableError

This Python version does not support Windows named pipes.

register classmethod

register(
    name: str, *aliases: str
) -> Callable[
    [SSHAgentSocketProviderT], SSHAgentSocketProviderT
]

Register a callable as an SSH agent socket provider (decorator).

Attempting to re-register an existing alias, or a name with an implementation, with a different implementation is an error.

Parameters:

Name Type Description Default
name str

The principal name under which to register the passed callable.

required
aliases str

Alternate names to register as aliases for the principal name.

()

Returns:

Type Description
Callable[[SSHAgentSocketProviderT], SSHAgentSocketProviderT]

A decorator implementing the above.

lookup classmethod

lookup(
    provider: SSHAgentSocketProvider | str | None,
) -> SSHAgentSocketProvider | None

Look up a socket provider entry.

Parameters:

Name Type Description Default
provider SSHAgentSocketProvider | str | None

The provider to look up.

required

Returns:

Type Description
SSHAgentSocketProvider | None

The callable indicated by this provider, if it is implemented, or None, if it is merely registered.

Raises:

Type Description
NoSuchProviderError

The provider is not registered.

resolve classmethod

resolve(
    provider: SSHAgentSocketProvider | str | None,
) -> SSHAgentSocketProvider

Resolve a socket provider to a proper callable.

Parameters:

Name Type Description Default
provider SSHAgentSocketProvider | str | None

The provider to resolve.

required

Returns:

Type Description
SSHAgentSocketProvider

The callable indicated by this provider.

Raises:

Type Description
NoSuchProviderError

The provider is not registered.

NotImplementedError

The provider is registered, but is not functional or not applicable to this derivepassphrase installation.

grouped classmethod

grouped() -> Mapping[str, Set[str]]

Calculate a mapping of canonical socket provider entries.

Specifically, determine the non-alias entries in the socket provider registry, and map each such non-alias entry to its set of aliases.

Returns:

Type Description
Mapping[str, Set[str]]

A mapping of non-alias entry names to sets of alias entry names.

Warning

The results are undefined if the registry has been modified by any means other than the register decorator.