Python example for quote_generate

The following example shows how you can call the quote_generate syscall from Python code using the ctypes library.

import ctypes
import errno
import os
import sys

class SgxQuote:
    __NR_syscall_quote = 371      # Custom syscall implemented by the Anjuna SGX Runtime
    MAX_QUOTE_SIZE = 1024 * 8     # DCAP quotes are typically around 4KB => overallocate

    @classmethod
    def generate(cls, userdata: bytes) -> bytes:
        """
        Invoke Anjuna SGX Runtime custom syscall 'quote_generate'
        """

        # Create a syscall function with all NULL parameters after the request type
        quote_syscall = ctypes.CDLL(None, use_errno=True).syscall
        quote_syscall.restype = ctypes.c_int
        quote_syscall.argtypes = (
            ctypes.c_long,                  # Syscall number
            ctypes.POINTER(ctypes.c_ubyte), # userdata
            ctypes.c_size_t,                # userdata length
            ctypes.POINTER(ctypes.c_char),  # output buffer
            ctypes.c_size_t,                # output buffer max size
            ctypes.c_void_p,                # NULL
            ctypes.c_void_p                 # NULL
        )

        raw_bytes = (ctypes.c_ubyte * len(userdata)).from_buffer_copy(userdata)
        c_quote_buffer = ctypes.create_string_buffer(b'', cls.MAX_QUOTE_SIZE)
        syscall_args = [
            ctypes.c_long(cls.__NR_syscall_quote),
            raw_bytes,
            len(raw_bytes),
            c_quote_buffer,
            ctypes.c_size_t(cls.MAX_QUOTE_SIZE),
            ctypes.c_void_p(0),
            ctypes.c_void_p(0)
        ]

        # Invoke the syscall
        ret = quote_syscall(*syscall_args)
        if ret < 0:
            # Retrieve the ERRNO + string describing the error
            syscall_error = ctypes.get_errno()
            str_error = "unknown error code"
            if syscall_error in errno.errorcode:
                str_error = errno.errorcode[syscall_error]

            if syscall_error == errno.ENOSYS:
                raise RuntimeError("Anjuna syscall 'quote_generate' not implemented. Are you running with the Anjuna SGX Runtime?")
            else:
                raise ValueError(f"Error generating SGX attestation quote (error code={syscall_error} {str_error})")
        else:
            # There is a quote: the return value is the length of the quote
            quote_length = ret
            quote = c_quote_buffer.raw[:quote_length]
            return quote

if __name__ == '__main__':
    quote = SgxQuote.generate(userdata=b"example userdata")
    # then, do something with the quote