derivepassphrase.ssh_agent
¶
A bare-bones SSH agent client supporting signing and key listing.
TrailingDataError
¶
SSHAgentFailedError
¶
Bases: RuntimeError
The SSH agent failed to complete the requested operation.
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 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.
__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
¶
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 |
Examples:
>>> SSHAgentClient.uint32(16777216)
b'\x01\x00\x00\x00'
string
classmethod
¶
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
¶
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
¶
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 |
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
|
Yields:
| Type | Description |
|---|---|
SSHAgentClient
|
When entering this context, return the SSH agent client. |
Raises:
| Type | Description |
|---|---|
NoSuchProviderError
|
As per |
KeyError
|
As per |
NotImplementedError
|
As per |
OSError
|
As per |
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] | 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
¶
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 |
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 |
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
|
|
query_extensions
¶
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.
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. |
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
¶
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
If true, and if we fail to call |
False
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
|
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
¶
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 |
UnixDomainSocketsNotAvailableError
|
This Python version does not support UNIX domain
sockets, necessary to automatically connect to
a running SSH agent via the |
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 |
ValueError
|
The path in |
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 |
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 |
grouped
classmethod
¶
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.