pycape.cape module#

The Cape Python client.

The Cape client uses websockets to connect to Cape enclaves that are hosting a user’s deployed functions. Before being able to run functions from the Cape client, users must have gone through the process of developing a Cape function in Python, deploying it from the CLI, and generating a personal access token.

All public async methods in the Cape client interface can be used in either synchronous or asynchronous contexts via asyncio.

Usage

cape = Cape()

f = cape.function("user/pythagorean")  # refer to function by name
t = cape.token("user.token")           # load function owner's PAT
cape.connect(f, t)

c1 = cape.invoke(3, 4, use_serdio=True)
print(c1)  # 5

c2 = cape.invoke(5, 12, use_serdio=True)
print(c2)  # 13

cape.close()  # release the enclave connection

# similar invocation, but async
c3 = asyncio.run(
    cape.run(f, t, 8, 15, use_serdio=True)
)
print(c3)  # 17
class pycape.cape.Cape(url=None, verbose=False)[source]#

Bases: object

A websocket client for interacting with enclaves hosting Cape functions.

This is the main interface for interacting with Cape functions from Python. See module-level documentation pycape.cape for usage example.

Parameters:
  • url (Optional[str]) – The Cape platform’s websocket URL, which is responsible for forwarding client requests to the proper enclave instances. If None, tries to load value from the CAPE_ENCLAVE_HOST environment variable. If no such variable value is supplied, defaults to "https://app.capeprivacy.com".

  • verbose (bool) – Boolean controlling verbose logging for the "pycape" logger. If True, sets log-level to DEBUG.

close()[source]#

Closes the current enclave connection.

connect(function_ref, token, pcrs=None)[source]#

Connects to the enclave hosting the function denoted by function_ref.

Note that this method creates a stateful websocket connection, which is a necessary precondition for callers of invoke(). When using the default Cape host, the enclave will terminate this websocket connection after 60s of inactivity. Care should be taken to close the websocket connection with close() once all invocations have finished.

Parameters:
  • function_ref (Union[str, PathLike, FunctionRef]) – Reference to a Cape deployed function. Must be convertible to a FunctionRef. See Cape.function() for a description of recognized values.

  • token (Union[str, PathLike, Token]) – Personal Access Token scoped for the given Cape function. Must be convertible to Token, see Cape.token() for a description of recognized values.

  • pcrs (Optional[Dict[str, List[str]]]) – An optional dictionary of PCR indexes to a list of expected or allowed PCRs.

Raises:
  • RuntimeError – if the websocket response or the enclave attestation doc is malformed, or if the enclave fails to return a function checksum matching our own.

  • Exception – if the enclave threw an error while trying to fulfill the connection request.

encrypt(input, *, username=None, key=None, key_path=None)[source]#

Encrypts inputs to Cape functions in Cape’s encryption format.

The encrypted value can be used as input to Cape handlers by other callers of invoke() or run() without giving them plaintext access to it. The core encryption functionality uses envelope encryption; the value is AES-encrypted with an ephemeral AES key, which is itself encrypted with the Cape user’s assigned RSA public key. The corresponding RSA private key is only accessible from within a Cape enclave, which guarantees secrecy of the encrypted value. See the Cape encrypt docs for further detail.

Parameters:
  • input (bytes) – Input bytes to encrypt.

  • username (Optional[str]) – A Github username corresponding to a Cape user who’s public key you want to use for the encryption. See Cape.key() for details.

  • key (Optional[bytes]) – Optional bytes for the Cape key. If None, will delegate to calling Cape.key() w/ the given key_path to retrieve the user’s Cape key.

  • key_path (Union[str, PathLike, None]) – Optional path to a locally-cached Cape key. See Cape.key() for details.

Return type:

bytes

Returns:

Tagged ciphertext representing a base64-encoded Cape encryption of the input.

Raises:
  • ValueError – if Cape key is not a properly-formatted RSA public key.

  • RuntimeError – if the enclave attestation doc does not contain a Cape key, if the websocket response or the attestation doc is malformed.

  • Exception – if the enclave threw an error while trying to fulfill the connection request.

function(identifier, *, checksum=None)[source]#

Convenience function for creating a FunctionRef.

The identifier parameter is interepreted according to the following priority:

  • Filepath to a FunctionRef JSON. See from_json() for expected JSON structure.

  • String representing a function ID

  • String of the form “{username}/{fn_name}” representing a function name.

  • A FunctionRef. If its checksum is missing and a checksum argument is given, it will be added to the returned value.

Parameters:
  • identifier (Union[str, PathLike, FunctionRef]) – A string identifier that can be converted into a FunctionRef. See above for options.

  • checksum (Optional[str]) – keyword-only argument for the function checksum. Ignored if identifier points to a JSON.

Return type:

FunctionRef

function_context(function_ref, token, pcrs=None)[source]#

Creates a context manager for a given function_ref’s enclave connection.

Note that this context manager accomplishes the same functionality as connect(), except that it will also automatically close() the connection when exiting the context.

Usage

cape = Cape(url="https://app.capeprivacy.com")
f = cape.function("function.json)
t = cape.token("pycape-dev.token")

with cape.function_context(f, t):

    c1 = cape.invoke(3, 4, use_serdio=True)
    print(c1)  # 5

    c2 = cape.invoke(5, 12, use_serdio=True)
    print(c2)  # 13

# websocket connection is automatically closed
Parameters:

function_ref (Union[str, PathLike, FunctionRef]) – A function ID or FunctionRef representing a deployed Cape function.

Raises:
  • RuntimeError – if the websocket response or the enclave attestation doc is malformed, or if the enclave fails to return a function checksum matching our own.

  • Exception – if the enclave threw an error while trying to fulfill the connection request.

invoke(*args, serde_hooks=None, use_serdio=False, **kwargs)[source]#

Invokes a function call from the currently connected websocket.

This method assumes that the client is currently maintaining an open websocket connection to an enclave hosting a particular Cape function. Care should be taken to ensure that the function_red that spawned the connection is the correct one. The connection should be closed with close() once the caller is finished with its invocations.

Parameters:
  • *args (Any) – Arguments to pass to the connected Cape function. If use_serdio=False, we expect a single argument of type bytes. Otherwise, these arguments should match the positional arguments of the undecorated Cape handler, and they will be auto-serialized by Serdio before being sent in the request.

  • serde_hooks – An optional pair of serdio encoder/decoder hooks convertible to serdio.SerdeHookBundle. The hooks are necessary if the args / kwargs have any user-defined types that can’t be handled by vanilla Serdio. See serdio.bundle_serde_hooks() for supported types.

  • use_serdio (bool) – Boolean controlling whether or not the inputs should be auto-serialized by serdio.

  • kwargs (Any) – Keyword arguments to be passed to the connected Cape function. These are treated the same way as the args are.

Return type:

Any

Returns:

If use_serdio=True, returns the auto-deserialized result of calling the connected Cape function on the given args / kwargs. If use_serdio=False, returns the output of the Cape function as raw bytes.

Raises:

RuntimeError – if serialized inputs could not be HPKE-encrypted, or if websocket response is malformed.

key(*, username=None, key_path=None, pcrs=None)[source]#

Load a Cape key from disk or download and persist an enclave-generated one.

If no username or key_path is provided, will try to load the currently logged-in CLI user’s key from a local cache.

Parameters:
  • username (Optional[str]) – An optional string representing the Github username of a Cape user. The resulting public key will be associated with their account, and data encrypted with this key will be available inside functions that user has deployed.

  • key_path (Union[str, PathLike, None]) – The path to the Cape key file. If the file already exists, the key will be read from disk and returned. Otherwise, a Cape key will be requested from the Cape platform and written to this location. If None, the default path is "$HOME/.config/cape/capekey.pub.der", or alternatively whatever path is specified by expanding the env variables CAPE_LOCAL_CONFIG_DIR / CAPE_LOCAL_CAPE_KEY_FILENAME.

  • pcrs (Optional[Dict[str, List[str]]]) – A dictionary of PCR indexes to a list of potential values.

Return type:

bytes

Returns:

Bytes containing the Cape key. The key is also cached on disk for later use.

Raises:
  • RuntimeError – if the enclave attestation doc does not contain a Cape key, if the websocket response or the attestation doc is malformed.

  • Exception – if the enclave threw an error while trying to fulfill the connection request.

run(function_ref, token, *args, pcrs=None, serde_hooks=None, use_serdio=False, **kwargs)[source]#

Single-shot version of connect + invoke + close.

This method takes care of establishing a websocket connection via connect(), invoking it via invoke(), and then finally closing the connection with close(). This method should be preferred when the caller doesn’t need to invoke a Cape function more than once.

Parameters:
  • function_ref (Union[str, PathLike, FunctionRef]) – A value convertible to a FunctionRef, representing a deployed Cape function. See Cape.function() for recognized values.

  • *args (Any) – Arguments to pass to the connected Cape function. If use_serdio=False, we expect a single argument of type bytes. Otherwise, these arguments should match the positional arguments of the undecorated Cape handler, and they will be auto-serialized by Serdio before being sent in the request.

  • serde_hooks – An optional pair of serdio encoder/decoder hooks convertible to serdio.SerdeHookBundle. The hooks are necessary if the args / kwargs have any user-defined types that can’t be handled by vanilla Serdio. See serdio.bundle_serde_hooks() for supported types.

  • use_serdio (bool) – Boolean controlling whether or not the inputs should be auto-serialized by serdio.

  • kwargs (Any) – Keyword arguments to be passed to the connected Cape function. These are treated the same way as the args are.

Return type:

Any

Returns:

If use_serdio=True, returns the auto-deserialized result of calling the connected Cape function on the given args / kwargs. If use_serdio=False, returns the output of the Cape function as raw bytes.

Raises:

RuntimeError – if serialized inputs could not be HPKE-encrypted, or if websocket response is malformed.

token(token)[source]#

Create or load a Token.

Parameters:

token (Union[str, PathLike, Token]) – Filepath to a token file, or the raw token string itself.

Return type:

Token

Returns:

A Token that can be used to access users’ deployed Cape functions.

Raises:

TypeError – if the token argument type is unrecognized.