Skip to content

Submodule ssh_agent

derivepassphrase.ssh_agent

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

TrailingDataError

TrailingDataError()

Bases: RuntimeError

The result contained trailing data.

SSHAgentFailedError

Bases: RuntimeError

The SSH agent failed to complete the requested operation.

SSHAgentClient

SSHAgentClient(
    *, socket: socket | None = None, timeout: int = 125
)

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 socket | None

An optional socket, already connected to the SSH agent. If not given, we query the SSH_AUTH_SOCK environment variable to auto-discover the correct socket address.

We currently only support connecting via UNIX domain sockets, and only on platforms with support for socket.AF_UNIX.

None
timeout int

A connection timeout for the SSH agent. Only used 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

Raises:

Type Description
KeyError

The SSH_AUTH_SOCK environment variable was not found.

NotImplementedError

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.

__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'')

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}
    ),
) -> tuple[int, 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[KeyCommentPair]

Request a list of keys known to the SSH agent.

Returns:

Type Description
Sequence[KeyCommentPair]

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.