Add PSA crypto sim client and server implementations of psa_hash_compute()

A Perl script that creates them is also included as reference. This is not the
final script (that will be in Python) but a proof-of-concept to show that
creaation client and server wrappers can be scripted.

It is not hooked into the build: it must be run manually. It is not part of the
deliverables for this PR.

Signed-off-by: Tom Cosgrove <tom.cosgrove@arm.com>
diff --git a/tests/psa-client-server/psasim/src/psa_functions_codes.h b/tests/psa-client-server/psasim/src/psa_functions_codes.h
index 34897b9..9306be9 100644
--- a/tests/psa-client-server/psasim/src/psa_functions_codes.h
+++ b/tests/psa-client-server/psasim/src/psa_functions_codes.h
@@ -1,9 +1,18 @@
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
 #ifndef _PSA_FUNCTIONS_CODES_H_
 #define  _PSA_FUNCTIONS_CODES_H_
 
 enum {
-    PSA_CRYPTO_INIT = 0x00,
-    /* Add other PSA functions here */
+    /* Start here to avoid overlap with PSA_IPC_CONNECT, PSA_IPC_DISCONNECT
+     * and VERSION_REQUEST */
+    PSA_CRYPTO_INIT = 100,
+    PSA_HASH_COMPUTE,
 };
 
 #endif /*  _PSA_FUNCTIONS_CODES_H_ */
diff --git a/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c
new file mode 100644
index 0000000..85c8a3c
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c
@@ -0,0 +1,207 @@
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/* client calls */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+/* Includes from psasim */
+#include <client.h>
+#include <util.h>
+#include "psa_manifest/sid.h"
+#include "psa_functions_codes.h"
+#include "psa_sim_serialise.h"
+
+/* Includes from mbedtls */
+#include "mbedtls/version.h"
+#include "psa/crypto.h"
+
+#define CLIENT_PRINT(fmt, ...) \
+    PRINT("Client: " fmt, ##__VA_ARGS__)
+
+static psa_handle_t handle = -1;
+
+int psa_crypto_call(int function,
+                    uint8_t *in_params, size_t in_params_len,
+                    uint8_t **out_params, size_t *out_params_len)
+{
+    // psa_outvec outvecs[1];
+    if (handle < 0) {
+        fprintf(stderr, "NOT CONNECTED\n");
+        exit(1);
+    }
+
+    psa_invec invec;
+    invec.base = in_params;
+    invec.len = in_params_len;
+
+    size_t max_receive = 8192;
+    uint8_t *receive = malloc(max_receive);
+    if (receive == NULL) {
+        fprintf(stderr, "FAILED to allocate %u bytes\n", (unsigned) max_receive);
+        exit(1);
+    }
+
+    size_t actual_received = 0;
+
+    psa_outvec outvecs[2];
+    outvecs[0].base = &actual_received;
+    outvecs[0].len = sizeof(actual_received);
+    outvecs[1].base = receive;
+    outvecs[1].len = max_receive;
+
+    psa_status_t status = psa_call(handle, function, &invec, 1, outvecs, 2);
+    if (status != PSA_SUCCESS) {
+        free(receive);
+        return 0;
+    }
+
+    *out_params = receive;
+    *out_params_len = actual_received;
+
+    return 1;   // success
+}
+
+psa_status_t psa_crypto_init(void)
+{
+    char mbedtls_version[18];
+    uint8_t *result = NULL;
+    size_t result_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_version_get_string_full(mbedtls_version);
+    CLIENT_PRINT("%s", mbedtls_version);
+
+    CLIENT_PRINT("My PID: %d", getpid());
+
+    CLIENT_PRINT("PSA version: %u", psa_version(PSA_SID_CRYPTO_SID));
+    handle = psa_connect(PSA_SID_CRYPTO_SID, 1);
+
+    if (handle < 0) {
+        CLIENT_PRINT("Couldn't connect %d", handle);
+        return PSA_ERROR_COMMUNICATION_FAILURE;
+    }
+
+    int ok = psa_crypto_call(PSA_CRYPTO_INIT, NULL, 0, &result, &result_length);
+    CLIENT_PRINT("PSA_CRYPTO_INIT returned: %d", ok);
+
+    if (!ok) {
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_length;
+
+    ok = psasim_deserialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_status_t(&rpos, &rremain, &status);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(result);
+
+    return status;
+}
+
+void mbedtls_psa_crypto_free(void)
+{
+    CLIENT_PRINT("Closing handle");
+    psa_close(handle);
+    handle = -1;
+}
+
+
+psa_status_t psa_hash_compute(
+    psa_algorithm_t alg,
+    const uint8_t *input, size_t  input_length,
+    uint8_t *hash, size_t  hash_size,
+    size_t *hash_length
+    )
+{
+    uint8_t *params = NULL;
+    uint8_t *result = NULL;
+    size_t result_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    size_t needed = psasim_serialise_begin_needs() +
+                    psasim_serialise_psa_algorithm_t_needs(alg) +
+                    psasim_serialise_buffer_needs(input, input_length) +
+                    psasim_serialise_buffer_needs(hash, hash_size) +
+                    psasim_serialise_size_t_needs(*hash_length);
+
+    params = malloc(needed);
+    if (params == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto fail;
+    }
+
+    uint8_t *pos = params;
+    size_t remaining = needed;
+    int ok;
+    ok = psasim_serialise_begin(&pos, &remaining);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_psa_algorithm_t(&pos, &remaining, alg);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_buffer(&pos, &remaining, input, input_length);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_buffer(&pos, &remaining, hash, hash_size);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_size_t(&pos, &remaining, *hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_COMPUTE,
+                         params, (size_t) (pos - params), &result, &result_length);
+    if (!ok) {
+        printf("XXX server call failed\n");
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_length;
+
+    ok = psasim_deserialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_status_t(&rpos, &rremain, &status);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_return_buffer(&rpos, &rremain, hash, hash_size);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_size_t(&rpos, &rremain, hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
diff --git a/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c
new file mode 100644
index 0000000..c15b2b0
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c
@@ -0,0 +1,225 @@
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/* server implementations */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <psa/crypto.h>
+
+#include "psa_functions_codes.h"
+#include "psa_sim_serialise.h"
+
+#include "service.h"
+
+// Returns 1 for success, 0 for failure
+int psa_crypto_init_wrapper(
+    uint8_t *in_params, size_t in_params_len,
+    uint8_t **out_params, size_t *out_params_len)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    uint8_t *result = NULL;
+    int ok;
+
+    // Now we call the actual target function
+
+    status = psa_crypto_init(
+        );
+
+    // NOTE: Should really check there is no overflow as we go along.
+    size_t result_size =
+        psasim_serialise_begin_needs() +
+        psasim_serialise_psa_status_t_needs(status);
+
+    result = malloc(result_size);
+    if (result == NULL) {
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_size;
+
+    ok = psasim_serialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_serialise_psa_status_t(&rpos, &rremain, status);
+    if (!ok) {
+        goto fail;
+    }
+
+    *out_params = result;
+    *out_params_len = result_size;
+
+    return 1;   // success
+
+fail:
+    free(result);
+    return 0;       // This shouldn't happen!
+}
+
+// Returns 1 for success, 0 for failure
+int psa_hash_compute_wrapper(
+    uint8_t *in_params, size_t in_params_len,
+    uint8_t **out_params, size_t *out_params_len)
+{
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    psa_algorithm_t alg;
+    uint8_t *input = NULL;
+    size_t input_length;
+    uint8_t *hash = NULL;
+    size_t hash_size;
+    size_t hash_length;
+
+    uint8_t *pos = in_params;
+    size_t remaining = in_params_len;
+    uint8_t *result = NULL;
+    int ok;
+
+    ok = psasim_deserialise_begin(&pos, &remaining);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_algorithm_t(&pos, &remaining, &alg);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_buffer(&pos, &remaining, &input, &input_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_buffer(&pos, &remaining, &hash, &hash_size);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_size_t(&pos, &remaining, &hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_compute(
+        alg,
+        input, input_length,
+        hash, hash_size,
+        &hash_length
+        );
+
+    // NOTE: Should really check there is no overflow as we go along.
+    size_t result_size =
+        psasim_serialise_begin_needs() +
+        psasim_serialise_psa_status_t_needs(status) +
+        psasim_serialise_buffer_needs(hash, hash_size) +
+        psasim_serialise_size_t_needs(hash_length);
+
+    result = malloc(result_size);
+    if (result == NULL) {
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_size;
+
+    ok = psasim_serialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_serialise_psa_status_t(&rpos, &rremain, status);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_serialise_buffer(&rpos, &rremain, hash, hash_size);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_serialise_size_t(&rpos, &rremain, hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    *out_params = result;
+    *out_params_len = result_size;
+
+    return 1;   // success
+
+fail:
+    free(result);
+    return 0;       // This shouldn't happen!
+}
+
+psa_status_t psa_crypto_call(psa_msg_t msg)
+{
+    int ok = 0;
+
+    int func = msg.type;
+
+    /* We only expect a single input buffer, with everything serialised in it */
+    if (msg.in_size[1] != 0 || msg.in_size[2] != 0 || msg.in_size[3] != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* We expect exactly 2 output buffers, one for size, the other for data */
+    if (msg.out_size[0] != sizeof(size_t) || msg.out_size[1] == 0 ||
+        msg.out_size[2] != 0 || msg.out_size[3] != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    uint8_t *in_params = NULL;
+    size_t in_params_len = 0;
+    uint8_t *out_params = NULL;
+    size_t out_params_len = 0;
+
+    in_params_len = msg.in_size[0];
+    in_params = malloc(in_params_len);
+    if (in_params == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    /* Read the bytes from the client */
+    size_t actual = psa_read(msg.handle, 0, in_params, in_params_len);
+    if (actual != in_params_len) {
+        free(in_params);
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    switch (func) {
+        case PSA_CRYPTO_INIT:
+            ok = psa_crypto_init_wrapper(in_params, in_params_len,
+                                         &out_params, &out_params_len);
+            break;
+        case PSA_HASH_COMPUTE:
+            ok = psa_hash_compute_wrapper(in_params, in_params_len,
+                                          &out_params, &out_params_len);
+            break;
+    }
+
+    free(in_params);
+
+    if (out_params_len > msg.out_size[1]) {
+        fprintf(stderr, "unable to write %zu bytes into buffer of %zu bytes\n",
+                out_params_len, msg.out_size[1]);
+        exit(1);
+    }
+
+    /* Write the exact amount of data we're returning */
+    psa_write(msg.handle, 0, &out_params_len, sizeof(out_params_len));
+
+    /* And write the data itself */
+    if (out_params_len) {
+        psa_write(msg.handle, 1, out_params, out_params_len);
+    }
+
+    free(out_params);
+
+    return ok ? PSA_SUCCESS : PSA_ERROR_GENERIC_ERROR;
+}
diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl
new file mode 100644
index 0000000..62c1a89
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl
@@ -0,0 +1,1130 @@
+#!/usr/bin/env perl
+#
+# This is a proof-of-concept script to show that the client and server wrappers
+# can be created by a script. It is not hooked into the build, so is run
+# manually and the output files are what are to be reviewed. In due course
+# this will be replaced by a Python script.
+#
+use strict;
+use Data::Dumper;
+use JSON qw(encode_json);
+
+my $debug = 0;
+
+# Globals (sorry!)
+my %functions = get_functions();
+my @functions = sort keys %functions;
+
+# get_functions(), called above, returns a data structure for each function
+# that we need to create client and server stubs for. In this example Perl script,
+# the function declarations we want are in the data section (after __END__ at
+# the bottom of this file), but a production Python version should process
+# psa_crypto.h.
+#
+# In this script, the data for psa_crypto_init() looks like:
+#
+#   "psa_crypto_init": {
+#     "return": {               # Info on return type
+#       "type": "psa_status_t", # Return type
+#       "name": "status",       # Name to be used for this in C code
+#       "default": "PSA_ERROR_CORRUPTION_DETECTED"      # Default value
+#     },
+#     "args": [],               # void function, so args empty
+#   }
+#
+# The data for psa_hash_compute() looks like:
+#
+#  "psa_hash_compute": {
+#    "return": {                # Information on return type
+#      "type": "psa_status_t",
+#      "name": "status",
+#      "default": "PSA_ERROR_CORRUPTION_DETECTED"
+#    },
+#    "args": [{
+#        "type": "psa_algorithm_t",             # Type of first argument
+#        "ctypename": "psa_algorithm_t ",       # C type with trailing spaces
+#                                               # (so that e.g. `char *` looks ok)
+#        "name": "alg",
+#        "is_output": 0
+#      }, {
+#        "type": "const buffer",                # Specially created
+#        "ctypename": "",                       # (so no C type)
+#        "name": "input, input_length",         # A pair of arguments
+#        "is_output": 0                         # const, so not an output argument
+#      }, {
+#        "type": "buffer",                      # Specially created
+#        "ctypename": "",
+#        "name": "hash, hash_size",
+#        "is_output": 1                         # Not const, so output argument
+#      }, {
+#        "type": "size_t",                      # size_t *hash_length
+#        "ctypename": "size_t ",
+#        "name": "*hash_length",                # * comes into the name
+#        "is_output": 1
+#      }
+#    ],
+#  },
+#
+# It's possible that a production version might not need both type and ctypename;
+# that was done for convenience and future-proofing during development.
+
+# We'll do psa_crypto_init() first
+put_crypto_init_first(\@functions);
+
+write_function_codes("psa_functions_codes.h");
+
+write_client_calls("psa_sim_crypto_client.c");
+
+write_server_implementations("psa_sim_crypto_server.c");
+
+sub write_function_codes
+{
+    my ($file) = @_;
+
+    open(my $fh, ">", $file) || die("$0: $file: $!\n");
+
+    # NOTE: psa_crypto_init() is written manually
+
+    print $fh <<EOF;
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#ifndef _PSA_FUNCTIONS_CODES_H_
+#define  _PSA_FUNCTIONS_CODES_H_
+
+enum {
+    /* Start here to avoid overlap with PSA_IPC_CONNECT, PSA_IPC_DISCONNECT
+     * and VERSION_REQUEST */
+    PSA_CRYPTO_INIT = 100,
+EOF
+
+    for my $function (@functions) {
+        my $enum = uc($function);
+        if ($enum ne "PSA_CRYPTO_INIT") {
+            print $fh <<EOF;
+    $enum,
+EOF
+        }
+    }
+
+    print $fh <<EOF;
+};
+
+#endif /*  _PSA_FUNCTIONS_CODES_H_ */
+EOF
+
+    close($fh);
+}
+
+sub write_client_calls
+{
+    my ($file) = @_;
+
+    open(my $fh, ">", $file) || die("$0: $file: $!\n");
+
+    print $fh client_calls_header();
+
+    for my $function (@functions) {
+        # psa_crypto_init() is hand written to establish connection to server
+        if ($function ne "psa_crypto_init") {
+            my $f = $functions{$function};
+            output_client($fh, $f, $function);
+        }
+    }
+
+    close($fh);
+}
+
+sub write_server_implementations
+{
+    my ($file) = @_;
+
+    open(my $fh, ">", $file) || die("$0: $file: $!\n");
+
+    print $fh server_implementations_header();
+
+    print $fh debug_functions() if $debug;
+
+    for my $function (@functions) {
+        my $f = $functions{$function};
+        output_server_wrapper($fh, $f, $function);
+    }
+
+    # Now output a switch statement that calls each of the wrappers
+
+    print $fh <<EOF;
+
+psa_status_t psa_crypto_call(psa_msg_t msg)
+{
+    int ok = 0;
+
+    int func = msg.type;
+
+    /* We only expect a single input buffer, with everything serialised in it */
+    if (msg.in_size[1] != 0 || msg.in_size[2] != 0 || msg.in_size[3] != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    /* We expect exactly 2 output buffers, one for size, the other for data */
+    if (msg.out_size[0] != sizeof(size_t) || msg.out_size[1] == 0 ||
+        msg.out_size[2] != 0 || msg.out_size[3] != 0) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    uint8_t *in_params = NULL;
+    size_t in_params_len = 0;
+    uint8_t *out_params = NULL;
+    size_t out_params_len = 0;
+
+    in_params_len = msg.in_size[0];
+    in_params = malloc(in_params_len);
+    if (in_params == NULL) {
+        return PSA_ERROR_INSUFFICIENT_MEMORY;
+    }
+
+    /* Read the bytes from the client */
+    size_t actual = psa_read(msg.handle, 0, in_params, in_params_len);
+    if (actual != in_params_len) {
+        free(in_params);
+        return PSA_ERROR_CORRUPTION_DETECTED;
+    }
+
+    switch (func) {
+EOF
+
+    for my $function (@functions) {
+        my $f = $functions{$function};
+        my $enum = uc($function);
+
+        # Create this call, in a way acceptable to uncustify:
+        #            ok = ${function}_wrapper(in_params, in_params_len,
+        #                                     &out_params, &out_params_len);
+        my $first_line = "            ok = ${function}_wrapper(in_params, in_params_len,";
+        my $idx = index($first_line, "(");
+        die("can't find (") if $idx < 0;
+        my $indent = " " x ($idx + 1);
+
+        print $fh <<EOF;
+        case $enum:
+$first_line
+$indent&out_params, &out_params_len);
+            break;
+EOF
+    }
+
+    print $fh <<EOF;
+    }
+
+    free(in_params);
+
+    if (out_params_len > msg.out_size[1]) {
+        fprintf(stderr, "unable to write %zu bytes into buffer of %zu bytes\\n",
+                out_params_len, msg.out_size[1]);
+        exit(1);
+    }
+
+    /* Write the exact amount of data we're returning */
+    psa_write(msg.handle, 0, &out_params_len, sizeof(out_params_len));
+
+    /* And write the data itself */
+    if (out_params_len) {
+        psa_write(msg.handle, 1, out_params, out_params_len);
+    }
+
+    free(out_params);
+
+    return ok ? PSA_SUCCESS : PSA_ERROR_GENERIC_ERROR;
+}
+EOF
+
+    close($fh);
+}
+
+sub server_implementations_header
+{
+    return <<'EOF';
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/* server implementations */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <psa/crypto.h>
+
+#include "psa_functions_codes.h"
+#include "psa_sim_serialise.h"
+
+#include "service.h"
+EOF
+}
+
+sub client_calls_header
+{
+    my $code = <<'EOF';
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/* client calls */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+/* Includes from psasim */
+#include <client.h>
+#include <util.h>
+#include "psa_manifest/sid.h"
+#include "psa_functions_codes.h"
+#include "psa_sim_serialise.h"
+
+/* Includes from mbedtls */
+#include "mbedtls/version.h"
+#include "psa/crypto.h"
+
+#define CLIENT_PRINT(fmt, ...) \
+    PRINT("Client: " fmt, ##__VA_ARGS__)
+
+static psa_handle_t handle = -1;
+EOF
+
+    $code .= debug_functions() if $debug;
+
+    $code .= <<'EOF';
+
+int psa_crypto_call(int function,
+                    uint8_t *in_params, size_t in_params_len,
+                    uint8_t **out_params, size_t *out_params_len)
+{
+    // psa_outvec outvecs[1];
+    if (handle < 0) {
+        fprintf(stderr, "NOT CONNECTED\n");
+        exit(1);
+    }
+
+    psa_invec invec;
+    invec.base = in_params;
+    invec.len = in_params_len;
+
+    size_t max_receive = 8192;
+    uint8_t *receive = malloc(max_receive);
+    if (receive == NULL) {
+        fprintf(stderr, "FAILED to allocate %u bytes\n", (unsigned) max_receive);
+        exit(1);
+    }
+
+    size_t actual_received = 0;
+
+    psa_outvec outvecs[2];
+    outvecs[0].base = &actual_received;
+    outvecs[0].len = sizeof(actual_received);
+    outvecs[1].base = receive;
+    outvecs[1].len = max_receive;
+
+    psa_status_t status = psa_call(handle, function, &invec, 1, outvecs, 2);
+    if (status != PSA_SUCCESS) {
+        free(receive);
+        return 0;
+    }
+
+    *out_params = receive;
+    *out_params_len = actual_received;
+
+    return 1;   // success
+}
+
+psa_status_t psa_crypto_init(void)
+{
+    char mbedtls_version[18];
+    uint8_t *result = NULL;
+    size_t result_length;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_version_get_string_full(mbedtls_version);
+    CLIENT_PRINT("%s", mbedtls_version);
+
+    CLIENT_PRINT("My PID: %d", getpid());
+
+    CLIENT_PRINT("PSA version: %u", psa_version(PSA_SID_CRYPTO_SID));
+    handle = psa_connect(PSA_SID_CRYPTO_SID, 1);
+
+    if (handle < 0) {
+        CLIENT_PRINT("Couldn't connect %d", handle);
+        return PSA_ERROR_COMMUNICATION_FAILURE;
+    }
+
+    int ok = psa_crypto_call(PSA_CRYPTO_INIT, NULL, 0, &result, &result_length);
+    CLIENT_PRINT("PSA_CRYPTO_INIT returned: %d", ok);
+
+    if (!ok) {
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_length;
+
+    ok = psasim_deserialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_status_t(&rpos, &rremain, &status);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(result);
+
+    return status;
+}
+
+void mbedtls_psa_crypto_free(void)
+{
+    CLIENT_PRINT("Closing handle");
+    psa_close(handle);
+    handle = -1;
+}
+EOF
+}
+
+sub debug_functions
+{
+    return <<EOF;
+
+static inline char hex_digit(char nibble) {
+    return (nibble < 10) ? (nibble + '0') : (nibble + 'a' - 10);
+}
+
+int hex_byte(char *p, uint8_t b)
+{
+    p[0] = hex_digit(b >> 4);
+    p[1] = hex_digit(b & 0x0F);
+
+    return 2;
+}
+
+int hex_uint16(char *p, uint16_t b)
+{
+    hex_byte(p, b >> 8);
+    hex_byte(p + 2, b & 0xFF);
+
+    return 4;
+}
+
+char human_char(uint8_t c)
+{
+    return (c >= ' ' && c <= '~') ? (char)c : '.';
+}
+
+void dump_buffer(const uint8_t *buffer, size_t len)
+{
+    char line[80];
+
+    const uint8_t *p = buffer;
+
+    size_t max = (len > 0xFFFF) ? 0xFFFF : len;
+
+    for (size_t i = 0; i < max; i += 16) {
+
+        char *q = line;
+
+        q += hex_uint16(q, (uint16_t)i);
+        *q++ = ' ';
+        *q++ = ' ';
+
+        size_t ll = (i + 16 > max) ? (max % 16) : 16;
+
+        size_t j;
+        for (j = 0; j < ll; j++) {
+            q += hex_byte(q, p[i + j]);
+            *q++ = ' ';
+        }
+
+        while (j++ < 16) {
+            *q++ = ' ';
+            *q++ = ' ';
+            *q++ = ' ';
+        }
+
+        *q++ = ' ';
+
+        for (j = 0; j < ll; j++) {
+            *q++ = human_char(p[i + j]);
+        }
+
+        *q = '\\0';
+
+        printf("%s\\n", line);
+    }
+}
+
+void hex_dump(uint8_t *p, size_t n)
+{
+    for (size_t i = 0; i < n; i++) {
+        printf("0x%02X ", p[i]);
+    }
+    printf("\\n");
+}
+EOF
+}
+
+sub output_server_wrapper
+{
+    my ($fh, $f, $name) = @_;
+
+    my $ret_type = $f->{return}->{type};
+    my $ret_name = $f->{return}->{name};
+    my $ret_default = $f->{return}->{default};
+
+    print $fh <<EOF;
+
+// Returns 1 for success, 0 for failure
+int ${name}_wrapper(
+    uint8_t *in_params, size_t in_params_len,
+    uint8_t **out_params, size_t *out_params_len)
+{
+    $ret_type $ret_name = $ret_default;
+EOF
+    # Output the variables we will need when we call the target function
+
+    my $args = $f->{args};
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        $argtype =~ s/^const //;
+
+        if ($argtype =~ /^(const )?buffer$/) {
+            my ($n1, $n2) = split(/,\s*/, $argname);
+            print $fh <<EOF;
+    uint8_t *$n1 = NULL;
+    size_t $n2;
+EOF
+        } else {
+            $argname =~ s/^\*//;        # Remove any leading *
+            print $fh <<EOF;
+    $argtype $argname;
+EOF
+        }
+    }
+
+    print $fh "\n";
+
+    if ($#$args >= 0) {          # If we have any args (>= 0)
+        print $fh <<EOF;
+    uint8_t *pos = in_params;
+    size_t remaining = in_params_len;
+EOF
+    }
+
+    print $fh <<EOF;
+    uint8_t *result = NULL;
+    int ok;
+EOF
+
+    print $fh <<EOF if $debug;
+
+    printf("$name: server\\n");
+EOF
+    if ($#$args >= 0) {          # If we have any args (>= 0)
+        print $fh <<EOF;
+
+    ok = psasim_deserialise_begin(&pos, &remaining);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+    }
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#$args) ? ";" : " +";
+        $argtype =~ s/^const //;
+
+        if ($argtype =~ /^(const )?buffer$/) {
+            my ($n1, $n2) = split(/,\s*/, $argname);
+            print $fh <<EOF;
+
+    ok = psasim_deserialise_${argtype}(&pos, &remaining, &$n1, &$n2);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        } else {
+            $argname =~ s/^\*//;        # Remove any leading *
+            print $fh <<EOF;
+
+    ok = psasim_deserialise_${argtype}(&pos, &remaining, &$argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        }
+    }
+
+    print $fh <<EOF;
+
+    // Now we call the actual target function
+EOF
+    output_call($fh, $f, $name);
+
+    my @outputs = grep($_->{is_output}, @$args);
+
+    my $sep1 = ($ret_type eq "void") ? ";" : " +";
+
+    print $fh <<EOF;
+
+    // NOTE: Should really check there is no overflow as we go along.
+    size_t result_size =
+        psasim_serialise_begin_needs()$sep1
+EOF
+
+    if ($ret_type ne "void") {
+        my $sep = ($#outputs < 0) ? ";" : " +";
+        print $fh <<EOF;
+        psasim_serialise_${ret_type}_needs($ret_name)$sep
+EOF
+    }
+
+    for my $i (0 .. $#outputs) {
+        my $arg = $outputs[$i];
+        die("$i: this should have been filtered out by grep") unless $arg->{is_output};
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#outputs) ? ";" : " +";
+        $argtype =~ s/^const //;
+        $argname =~ s/^\*//;        # Remove any leading *
+
+        print $fh <<EOF;
+        psasim_serialise_${argtype}_needs($argname)$sep
+EOF
+    }
+
+    print $fh <<EOF;
+
+    result = malloc(result_size);
+    if (result == NULL) {
+        goto fail;
+    }
+
+    uint8_t *rpos = result;
+    size_t rremain = result_size;
+
+    ok = psasim_serialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+
+    if ($ret_type ne "void") {
+        print $fh <<EOF;
+
+    ok = psasim_serialise_${ret_type}(&rpos, &rremain, $ret_name);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+    }
+
+    my @outputs = grep($_->{is_output}, @$args);
+
+    for my $i (0 .. $#outputs) {
+        my $arg = $outputs[$i];
+        die("$i: this should have been filtered out by grep") unless $arg->{is_output};
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#outputs) ? ";" : " +";
+        $argtype =~ s/^const //;
+
+        if ($argtype eq "buffer") {
+            print $fh <<EOF;
+
+    ok = psasim_serialise_buffer(&rpos, &rremain, $argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        } else {
+            if ($argname =~ /^\*/) {
+                $argname =~ s/^\*//;    # since it's already a pointer
+            } else {
+                die("$0: $argname: HOW TO OUTPUT?\n");
+            }
+
+            print $fh <<EOF;
+
+    ok = psasim_serialise_${argtype}(&rpos, &rremain, $argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        }
+    }
+
+    print $fh <<EOF;
+
+    *out_params = result;
+    *out_params_len = result_size;
+
+    return 1;   // success
+
+fail:
+    free(result);
+    return 0;       // This shouldn't happen!
+}
+EOF
+}
+
+sub output_client
+{
+    my ($fh, $f, $name) = @_;
+
+    print $fh "\n";
+
+    output_definition_begin($fh, $f, $name);
+
+    my $ret_type = $f->{return}->{type};
+    my $ret_name = $f->{return}->{name};
+    my $ret_default = $f->{return}->{default};
+
+    print $fh <<EOF;
+{
+    uint8_t *params = NULL;
+    uint8_t *result = NULL;
+    size_t result_length;
+    $ret_type $ret_name = $ret_default;
+EOF
+
+    print $fh <<EOF if $debug;
+
+    printf("$name: client\\n");
+EOF
+
+    print $fh <<EOF;
+
+    size_t needed = psasim_serialise_begin_needs() +
+EOF
+
+    my $args = $f->{args};
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#$args) ? ";" : " +";
+        $argtype =~ s/^const //;
+
+        print $fh <<EOF;
+                    psasim_serialise_${argtype}_needs($argname)$sep
+EOF
+    }
+
+    print $fh <<EOF;
+
+    params = malloc(needed);
+    if (params == NULL) {
+        status = PSA_ERROR_INSUFFICIENT_MEMORY;
+        goto fail;
+    }
+
+    uint8_t *pos = params;
+    size_t remaining = needed;
+    int ok;
+    ok = psasim_serialise_begin(&pos, &remaining);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#$args) ? ";" : " +";
+        $argtype =~ s/^const //;
+
+        print $fh <<EOF;
+    ok = psasim_serialise_${argtype}(&pos, &remaining, $argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+    }
+
+    print $fh <<EOF if $debug;
+
+    printf("client sending %d:\\n", (int)(pos - params));
+    dump_buffer(params, (size_t)(pos - params));
+EOF
+
+    my $enum = uc($name);
+
+    print $fh <<EOF;
+
+    ok = psa_crypto_call($enum,
+                         params, (size_t) (pos - params), &result, &result_length);
+    if (!ok) {
+        printf("XXX server call failed\\n");
+        goto fail;
+    }
+EOF
+
+    print $fh <<EOF if $debug;
+
+    printf("client receiving %d:\\n", (int)result_length);
+    dump_buffer(result, result_length);
+EOF
+
+    print $fh <<EOF;
+
+    uint8_t *rpos = result;
+    size_t rremain = result_length;
+
+    ok = psasim_deserialise_begin(&rpos, &rremain);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+
+    print $fh <<EOF;
+
+    ok = psasim_deserialise_$ret_type(&rpos, &rremain, &$ret_name);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+
+    my @outputs = grep($_->{is_output}, @$args);
+
+    for my $i (0 .. $#outputs) {
+        my $arg = $outputs[$i];
+        die("$i: this should have been filtered out by grep") unless $arg->{is_output};
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+        my $sep = ($i == $#outputs) ? ";" : " +";
+        $argtype =~ s/^const //;
+
+        if ($argtype eq "buffer") {
+            print $fh <<EOF;
+
+    ok = psasim_deserialise_return_buffer(&rpos, &rremain, $argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        } else {
+            if ($argname =~ /^\*/) {
+                $argname =~ s/^\*//;    # since it's already a pointer
+            } else {
+                die("$0: $argname: HOW TO OUTPUT?\n");
+            }
+
+            print $fh <<EOF;
+
+    ok = psasim_deserialise_${argtype}(&rpos, &rremain, $argname);
+    if (!ok) {
+        goto fail;
+    }
+EOF
+        }
+    }
+    print $fh <<EOF;
+
+fail:
+    free(params);
+    free(result);
+
+    return $ret_name;
+}
+EOF
+}
+
+sub output_declaration
+{
+    my ($f, $name) = @_;
+
+    output_signature($f, $name, "declaration");
+}
+
+sub output_definition_begin
+{
+    my ($fh, $f, $name) = @_;
+
+    output_signature($fh, $f, $name, "definition");
+}
+
+sub output_call
+{
+    my ($fh, $f, $name) = @_;
+
+    my $ret_name = $f->{return}->{name};
+    my $args = $f->{args};
+
+    print $fh "\n    $ret_name = $name(\n";
+
+    print $fh "        );\n" if $#$args < 0; # If no arguments, empty arg list
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};     # e.g. int, psa_algorithm_t, or "buffer"
+        my $argname = $arg->{name};
+
+        if ($argtype =~ /^(const )?buffer$/) {
+            my ($n1, $n2) = split(/,\s*/, $argname);
+            print $fh "        $n1, $n2";
+        } else {
+            $argname =~ s/^\*/\&/;      # Replace leading * with &
+            print $fh "        $argname";
+        }
+        my $sep = ($i == $#$args) ? "\n        );" : ",";
+        print $fh "$sep\n";
+    }
+}
+
+sub output_signature
+{
+    my ($fh, $f, $name, $what) = @_;
+
+    my $ret_type = $f->{return}->{type};
+    my $args = $f->{args};
+
+    my $final_sep = ($what eq "declaration") ? "\n);" : "\n    )";
+
+    print $fh "\n$ret_type $name(\n";
+
+    print $fh "    void\n)\n" if $#$args < 0;   # No arguments
+
+    for my $i (0 .. $#$args) {
+        my $arg = $args->[$i];
+        my $argtype = $arg->{type};             # e.g. int, psa_algorithm_t, or "buffer"
+        my $ctypename = $arg->{ctypename};      # e.g. "int ", "char *"; empty for buffer
+        my $argname = $arg->{name};
+
+        if ($argtype =~ /^(const )?buffer$/) {
+            my $const = length($1) ? "const " : "";
+            my ($n1, $n2) = split(/,/, $argname);
+            print $fh "    ${const}uint8_t *$n1, size_t $n2";
+        } else {
+            print $fh "    $ctypename$argname";
+        }
+        my $sep = ($i == $#$args) ? $final_sep : ",";
+        print $fh "$sep\n";
+    }
+}
+
+sub get_functions
+{
+    my $src = "";
+    while (<DATA>) {
+        chomp;
+        s/\/\/.*//;
+        s/\s+^//;
+        s/\s+/ /g;
+        $_ .= "\n";
+        $src .= $_;
+    }
+
+    $src =~ s/\/\*.*?\*\///gs;
+
+    my @src = split(/\n+/, $src);
+
+    my @rebuild = ();
+    my %funcs = ();
+    for (my $i = 0; $i <= $#src; $i++) {
+        my $line = $src[$i];
+        if ($line =~ /^psa_status_t (psa_\w*)\(/) { # begin function definition
+            #print "have one $line\n";
+            while ($line !~ /;/) {
+                $line .= $src[$i + 1];
+                $i++;
+            }
+            $line =~ s/\s+/ /g;
+            if ($line =~ /(\w+)\s+\b(\w+)\s*\(\s*(.*\S)\s*\)\s*[;{]/s) {
+                my ($ret_type, $func, $args) = ($1, $2, $3);
+                my $copy = $line;
+                $copy =~ s/{$//;
+                my $f = {
+                    "orig" => $copy,
+                };
+
+                my @args = split(/\s*,\s*/, $args);
+
+                my $ret_name = "";
+                $ret_name = "status" if $ret_type eq "psa_status_t";
+                die("ret_name for $ret_type?") unless length($ret_name);
+                my $ret_default = "";
+                $ret_default = "PSA_ERROR_CORRUPTION_DETECTED" if $ret_type eq "psa_status_t";
+                die("ret_default for $ret_type?") unless length($ret_default);
+
+                #print "FUNC $func RET_NAME $ret_name RET_TYPE $ret_type ARGS (", join("; ", @args), ")\n";
+
+                $f->{return} = {
+                    "type" => $ret_type,
+                    "default" => $ret_default,
+                    "name" => $ret_name,
+                };
+                $f->{args} = [];
+                # psa_algorithm_t alg; const uint8_t *input; size_t input_length; uint8_t *hash; size_t hash_size; size_t *hash_length
+                for (my $i = 0; $i <= $#args; $i++) {
+                    my $arg = $args[$i];
+                    # "type" => "psa_algorithm_t",
+                    # "ctypename" => "psa_algorithm_t ",
+                    # "name" => "alg",
+                    # "is_output" => 0,
+                    my ($type, $ctype, $name, $is_output);
+                    if ($arg =~ /^(\w+)\s+(\w+)$/) {    # e.g. psa_algorithm_t alg
+                        ($type, $name) = ($1, $2);
+                        $ctype = $type . " ";
+                        $is_output = 0;
+                    } elsif ($arg =~ /^((const)\s+)?uint8_t\s*\*\s*(\w+)$/) {
+                        $type = "buffer";
+                        $is_output = (length($1) == 0) ? 1 : 0;
+                        $type = "const buffer" if !$is_output;
+                        $ctype = "";
+                        $name = $3;
+                        #print("$arg: $name: might be a buffer?\n");
+                        die("$arg: not a buffer 1!\n") if $i == $#args;
+                        my $next = $args[$i + 1];
+                        die("$arg: not a buffer 2!\n") if $next !~ /^size_t\s+(${name}_\w+)$/;
+                        $i++;                   # We're using the next param here
+                        my $nname = $1;
+                        $name .= ", " . $nname;
+                    } elsif ($arg =~ /^((const)\s+)?(\w+)\s*\*(\w+)$/) {
+                        ($type, $name) = ($3, "*" . $4);
+                        $ctype = $1 . $type . " ";
+                        $is_output = (length($1) == 0) ? 1 : 0;
+                    } elsif ($arg eq "void") {
+                         # we'll just ignore this one
+                    } else {
+                        die("ARG HELP $arg\n");
+                    }
+                    #print "$arg => <$type><$ctype><$name><$is_output>\n";
+                    if ($arg ne "void") {
+                        push(@{$f->{args}}, {
+                            "type" => $type,
+                            "ctypename" => $ctype,
+                            "name" => $name,
+                            "is_output" => $is_output,
+                        });
+                    }
+                }
+                $funcs{$func} = $f;
+            } else {
+                die("FAILED");
+            }
+            push(@rebuild, $line);
+        } elsif ($line =~ /^static psa_\w+_t (psa_\w*)\(/) { # begin function definition
+             # IGNORE static functions
+        } else {
+            if ($line =~ /psa_/) {
+                print "NOT PARSED: $line\n";
+            }
+            push(@rebuild, $line);
+        }
+    }
+
+    #print ::Dumper(\%funcs);
+    #exit;
+
+    return %funcs;
+}
+
+sub put_crypto_init_first
+{
+    my ($functions) = @_;
+
+    my $want_first = "psa_crypto_init";
+
+    my $idx = undef;
+    for my $i (0 .. $#$functions) {
+        if ($functions->[$i] eq $want_first) {
+            $idx = $i;
+            last;
+        }
+    }
+
+    if (defined($idx) && $idx != 0) {   # Do nothing if already first
+        splice(@$functions, $idx, 1);
+        unshift(@$functions, $want_first);
+    }
+}
+
+__END__
+/**
+ * \brief Library initialization.
+ *
+ * Applications must call this function before calling any other
+ * function in this module.
+ *
+ * Applications may call this function more than once. Once a call
+ * succeeds, subsequent calls are guaranteed to succeed.
+ *
+ * If the application calls other functions before calling psa_crypto_init(),
+ * the behavior is undefined. Implementations are encouraged to either perform
+ * the operation as if the library had been initialized or to return
+ * #PSA_ERROR_BAD_STATE or some other applicable error. In particular,
+ * implementations should not return a success status if the lack of
+ * initialization may have security implications, for example due to improper
+ * seeding of the random number generator.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+psa_status_t psa_crypto_init(void);
+
+/** Calculate the hash (digest) of a message.
+ *
+ * \note To verify the hash of a message against an
+ *       expected value, use psa_hash_compare() instead.
+ *
+ * \param alg               The hash algorithm to compute (\c PSA_ALG_XXX value
+ *                          such that #PSA_ALG_IS_HASH(\p alg) is true).
+ * \param[in] input         Buffer containing the message to hash.
+ * \param input_length      Size of the \p input buffer in bytes.
+ * \param[out] hash         Buffer where the hash is to be written.
+ * \param hash_size         Size of the \p hash buffer in bytes.
+ * \param[out] hash_length  On success, the number of bytes
+ *                          that make up the hash value. This is always
+ *                          #PSA_HASH_LENGTH(\p alg).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported or is not a hash algorithm.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         \p hash_size is too small
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been previously initialized by psa_crypto_init().
+ *         It is implementation-dependent whether a failure to initialize
+ *         results in this error code.
+ */
+psa_status_t psa_hash_compute(psa_algorithm_t alg,
+                              const uint8_t *input,
+                              size_t input_length,
+                              uint8_t *hash,
+                              size_t hash_size,
+                              size_t *hash_length);