# Attesting Function Calls Blocky Attestation Service (Blocky AS) allows you to run custom functions in a Trusted Execution Environment (TEE) and receive attestations over their execution. In this example, we show you how to attest a simple "Hello, World!" function call using the Blocky AS CLI. ## Prerequisites - Install the [Blocky AS CLI](https://github.com/blocky/attestation-service-cli) and the [Blocky Compiler](https://github.com/blocky/compiler) by following the [Getting Started](/attestation-service#setup) instructions. - Make sure you have [Docker](https://www.docker.com/), [jq](https://jqlang.org/), and [openssl dgst](https://docs.openssl.org/3.4/man1/openssl-dgst/) installed on your system. - Clone the Blocky AS [examples repository](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12) and navigate to the `attest_fn_call` directory: git clone git@github.com:blocky/attestation-service-examples.git cd attestation-service-examples/attest_fn_call ## Step 1: Create a function that returns a "Hello, World!" message. Let's create a simple function that returns a `"Hello, World!"` message. We will write this function in Go and compile it to WebAssembly (WASM) to run on the Blocky AS server. If you look at [main.go](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/main.go) in this example's repository folder, you'll see our function there: helloWorld A few things to notice: - The `helloWorld` function is tagged with the `//export` comment, so that it can be invoked by the Blocky AS server in a WASM runtime. - The function takes two `uint64` arguments and returns a `uint64`. These are fat pointers to shared memory managed by the Blocky AS server, where the first 32 bits are a memory address and the second 32 bits are the size of the data. The memory space is sandboxed and shared between the TEE host program (Blocky AS server) and the WASM runtime (your function). The `inputPtr` and `secretPtr` arguments carry user-defined function input and secrets, though we don't make use of them in this example. The output of the function is also a memory pointer, whose value will be returned to the user. - The function calls the `basm` [Blocky Attestation Service WASM Go SDK](https://github.com/blocky/basm-go-sdk/tree/v0.1.0-beta.12) `WriteToHost` function to write a byte array (serialized from `msg`) to shared memory. The host (Blocky AS server) will create an attestation over that array as a part of its response. ## Step 2: Compile the function to WebAssembly (WASM) To invoke our function in the Blocky AS server, we need to compile it to WASM. If you inspect the `main.wasm` target in the [Makefile](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/Makefile), you'll see a series of commands that compile [main.go](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/main.go) to the `main.wasm` binary. You can build our function by calling: bky-c build --reproducible . ./main.wasm > Note that you can speed up the build time during development by dropping the `--reproducible` flag, which will allow `bky-c` to cache certain artifacts at the cost of reproducibility. ## Step 3: Invoke the function on the Blocky AS server To invoke our function, we need to define an invocation template. We set up the template in [fn-call.json](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/fn-call.json). You can inspect it by running: jq -r "." fn-call.json where `code_file` is the path to the WASM we built in the previous step, and `function` is the name of the exported function we want to call. To invoke our function, we need to pass the invocation template to `bky-as` CLI by calling: cat fn-call.json | bky-as attest-fn-call > out.json where we read and pipe the contents of [fn-call.json](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/fn-call.json) to the `bky-as attest-fn-call` command. The `bky-as` CLI will invoke our function on a Blocky AS server and save its response to `out.json`. > Note that the call to `bky-as attest-fn-call` will use the [config.toml](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/config.toml) configuration file. Its `host` field, set to `local-server`, will direct `bky-as` to start a local, non-TEE instance of the Blocky AS server to run our function in dev mode. You can confirm this by finding the following output: ``` 🚀 Starting local server at http://127.0.0.1:8081 ...success ``` If you'd like to send our function for execution on a Blocky AS server hosted on a TEE, you can follow the [Getting Started](/attestation-service#setup) instructions. ## Step 4: Extract function output from the Blocky AS attestation To extract the attested output of our `helloWorld` function, we need to parse the `out.json` file generated in the previous step. You can inspect it by running: jq -r '.' out.json The `enclave_attested_application_public_key` contains the `enclave_attestation` over the Blocky AS server public key. The `transitive_attested_function_call` section contains the `transitive_attestation` over the function call. The `bky-as attest-fn-call` command verifies that the `enclave_attestation` has been signed by either the TEE hardware manufacturer's private key, when the Blocky AS server is running on an AWS Nitro Enclave TEE, or by a hardcoded development private key, when the Blocky AS server is running locally. In this example, we have set up [config.toml](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/config.toml) with `host = "local-server"`, which directs the `bky-as` to start a local server. As a part of the verification process, the `bky-as` lists `enclave_attested_application_public_key.claims` attested by the `enclave_attestation`. You can confirm that `enclave_attestation` was produced by a local Blocky AS server by seeing that `enclave_attested_application_public_key.claims.enclave_measurement.platform` is reported as `plain`. The `bky-as` CLI also checks that the measurement of the Blocky AS server code, attested by the `enclave_attestation`, matches one in the `acceptable_measurements` list in [config.toml](https://github.com/blocky/attestation-service-examples/tree/release/v0.1.0-beta.12/attest_fn_call/config.toml). Again, since we are running the Blocky AS server locally in this example, the `enclave_attested_application_public_key.claims.enclave_measurement.code` is reported as `plain`. Finally, the `bky-as` CLI extracts the enclave attested application public key, generated by the Blocky AS server on startup, and uses it to verify the signature of the `transitive_attestation` (ABI-encoded in the above example) and extract its `claims`. You can learn more about this process in the [Attestations in the Blocky Attestation Service](/attestation-service/concepts#attestations-in-the-blocky-attestation-service) and [Verification Process](/attestation-service/verification#verification-process) sections in our documentation. ## Step 5: Verify transitive attested function call output The `claims` section contains attested information about the execution of the function. You can see: - `hash_of_code`: The hash of the WASM code executed by the Blocky AS server. In this example, `hash_of_code` is the hash of the `main.wasm` file we compiled in [Step 2](#step-2-compile-the-function-to-webassembly-wasm). You can verify this by running: BUILT_WASM_HASH=$(openssl dgst -sha3-512 main.wasm | awk '{print $2}') ATTESTED_WASM_HASH=$(jq -r '.transitive_attested_function_call.claims.hash_of_code' out.json) diff <(echo "$BUILT_WASM_HASH") <(echo "$ATTESTED_WASM_HASH") When the `diff` command returns no output, it means that the hashes match. > If the hashes don't match, check that you've used the `--reproducible` flag during the `bky-c` WASM build process. - `function`: The name of the function executed by the Blocky AS server. To parse out the function name from `out.json`, run: jq -r '.transitive_attested_function_call.claims.function' out.json - `hash_of_input`: The hash of the input data used by the function. In this example, `hash_of_input` is the hash of the empty string, since we didn't specify any input. You can verify this by running: BUILT_INPUT_HASH=$(echo -n "" | openssl dgst -sha3-512 | awk '{print $2}') ATTESTED_INPUT_HASH=$(jq -r '.transitive_attested_function_call.claims.hash_of_input' out.json) diff <(echo "$BUILT_INPUT_HASH") <(echo "$ATTESTED_INPUT_HASH") - `hash_of_secrets`: The hash of the decrypted secrets used by the function. In this example, `hash_of_secrets` is the hash of the empty string, since we didn't specify any secrets. You can verify this by running: BUILT_SECRETS_HASH=$(echo -n "" | openssl dgst -sha3-512 | awk '{print $2}') ATTESTED_SECRETS_HASH=$(jq -r '.transitive_attested_function_call.claims.hash_of_secrets' out.json) diff <(echo "$BUILT_SECRETS_HASH") <(echo "$ATTESTED_SECRETS_HASH") - `output`: The output of the function encoded in base64. To parse out the output of our `helloWorld` function, call: jq -r '.transitive_attested_function_call.claims.output | @base64d ' out.json rm -rf attestation-service-examples