pycape.cape module
Contents
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 theCAPE_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 toDEBUG
.
- 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 withclose()
once all invocations have finished.- Parameters:
function_ref (
Union
[str
,PathLike
,FunctionRef
]) – Reference to a Cape deployed function. Must be convertible to aFunctionRef
. SeeCape.function()
for a description of recognized values.token (
Union
[str
,PathLike
,Token
]) – Personal Access Token scoped for the given Cape function. Must be convertible toToken
, seeCape.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()
orrun()
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. SeeCape.key()
for details.key (
Optional
[bytes
]) – Optional bytes for the Cape key. If None, will delegate to callingCape.key()
w/ the givenkey_path
to retrieve the user’s Cape key.key_path (
Union
[str
,PathLike
,None
]) – Optional path to a locally-cached Cape key. SeeCape.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. Seefrom_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 achecksum
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 aFunctionRef
. See above for options.checksum (
Optional
[str
]) – keyword-only argument for the function checksum. Ignored ifidentifier
points to a JSON.
- Return type:
- 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 automaticallyclose()
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 orFunctionRef
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. Ifuse_serdio=False
, we expect a single argument of typebytes
. 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 theargs
/kwargs
have any user-defined types that can’t be handled by vanilla Serdio. Seeserdio.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 theargs
are.
- Return type:
Any
- Returns:
If
use_serdio=True
, returns the auto-deserialized result of calling the connected Cape function on the givenargs
/kwargs
. Ifuse_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 variablesCAPE_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 viainvoke()
, and then finally closing the connection withclose()
. 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 aFunctionRef
, representing a deployed Cape function. SeeCape.function()
for recognized values.*args (
Any
) – Arguments to pass to the connected Cape function. Ifuse_serdio=False
, we expect a single argument of typebytes
. 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 theargs
/kwargs
have any user-defined types that can’t be handled by vanilla Serdio. Seeserdio.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 theargs
are.
- Return type:
Any
- Returns:
If
use_serdio=True
, returns the auto-deserialized result of calling the connected Cape function on the givenargs
/kwargs
. Ifuse_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.