Merge pull request #9198 from tom-cosgrove-arm/psa-sim-hashing

Test psa_hash_xxx() APIs in the PSA crypto simulator
diff --git a/tests/psa-client-server/psasim/Makefile b/tests/psa-client-server/psasim/Makefile
index db0c412..06d3059 100644
--- a/tests/psa-client-server/psasim/Makefile
+++ b/tests/psa-client-server/psasim/Makefile
@@ -1,3 +1,5 @@
+MAIN ?= src/client.c
+
 CFLAGS += -Wall -Werror -std=c99 -D_XOPEN_SOURCE=1 -D_POSIX_C_SOURCE=200809L
 
 ifeq ($(DEBUG),1)
@@ -18,12 +20,16 @@
 					include/psa_manifest/sid.h
 
 PSA_CLIENT_SRC = src/psa_ff_client.c \
-				 src/client.c
+		 $(MAIN) \
+		 src/psa_sim_crypto_client.c \
+		 src/psa_sim_serialise.c
 
 PARTITION_SERVER_BOOTSTRAP = src/psa_ff_bootstrap_TEST_PARTITION.c
 
 PSA_SERVER_SRC = $(PARTITION_SERVER_BOOTSTRAP) \
-				 src/psa_ff_server.c
+				 src/psa_ff_server.c \
+				 src/psa_sim_crypto_server.c \
+				 src/psa_sim_serialise.c
 
 .PHONY: all clean libpsaclient libpsaserver
 
diff --git a/tests/psa-client-server/psasim/src/aut_psa_hash_compute.c b/tests/psa-client-server/psasim/src/aut_psa_hash_compute.c
new file mode 100644
index 0000000..519c072
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/aut_psa_hash_compute.c
@@ -0,0 +1,112 @@
+/*
+ *  API(s) under test: psa_hash_compute()
+ *
+ *  Taken from programs/psa/psa_hash.c, and calls to all hash APIs
+ *  but psa_hash_compute() removed.
+ *
+ *  Example computing a SHA-256 hash using the PSA Crypto API
+ *
+ *  The example computes the SHA-256 hash of a test string using the
+ *  one-shot API call psa_hash_compute().
+ *
+ *
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "psa/crypto.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mbedtls/build_info.h"
+#include "mbedtls/platform.h"
+
+/* Information about hashing with the PSA API can be
+ * found here:
+ * https://arm-software.github.io/psa-api/crypto/1.1/api/ops/hashes.html
+ *
+ * The algorithm used by this demo is SHA 256.
+ * Please see include/psa/crypto_values.h to see the other
+ * algorithms that are supported by Mbed TLS.
+ * If you switch to a different algorithm you will need to update
+ * the hash data in the EXAMPLE_HASH_VALUE macro below. */
+
+#if !defined(MBEDTLS_PSA_CRYPTO_C) || !defined(PSA_WANT_ALG_SHA_256)
+int main(void)
+{
+    mbedtls_printf("MBEDTLS_PSA_CRYPTO_C and PSA_WANT_ALG_SHA_256"
+                   "not defined.\r\n");
+    return EXIT_SUCCESS;
+}
+#else
+
+#define HASH_ALG PSA_ALG_SHA_256
+
+const uint8_t sample_message[] = "Hello World!";
+/* sample_message is terminated with a null byte which is not part of
+ * the message itself so we make sure to subtract it in order to get
+ * the message length. */
+const size_t sample_message_length = sizeof(sample_message) - 1;
+
+#define EXPECTED_HASH_VALUE {                                                    \
+        0x7f, 0x83, 0xb1, 0x65, 0x7f, 0xf1, 0xfc, 0x53, 0xb9, 0x2d, 0xc1, 0x81, \
+        0x48, 0xa1, 0xd6, 0x5d, 0xfc, 0x2d, 0x4b, 0x1f, 0xa3, 0xd6, 0x77, 0x28, \
+        0x4a, 0xdd, 0xd2, 0x00, 0x12, 0x6d, 0x90, 0x69 \
+}
+
+const uint8_t expected_hash[] = EXPECTED_HASH_VALUE;
+const size_t expected_hash_len = sizeof(expected_hash);
+
+int main(void)
+{
+    psa_status_t status;
+    uint8_t hash[PSA_HASH_LENGTH(HASH_ALG)];
+    size_t hash_length;
+
+    mbedtls_printf("PSA Crypto API: SHA-256 example\n\n");
+
+    status = psa_crypto_init();
+    if (status != PSA_SUCCESS) {
+        mbedtls_printf("psa_crypto_init failed\n");
+        return EXIT_FAILURE;
+    }
+
+    /* Clear local variables prior to one-shot hash demo */
+    memset(hash, 0, sizeof(hash));
+    hash_length = 0;
+
+    /* Compute hash using one-shot function call */
+    status = psa_hash_compute(HASH_ALG,
+                              sample_message, sample_message_length,
+                              hash, sizeof(hash),
+                              &hash_length);
+    if (status != PSA_SUCCESS) {
+        mbedtls_printf("psa_hash_compute failed\n");
+        goto cleanup;
+    }
+
+    if (hash_length != expected_hash_len ||
+        (memcmp(hash, expected_hash, expected_hash_len) != 0)) {
+        mbedtls_printf("One-shot hash operation gave the wrong result!\n\n");
+        goto cleanup;
+    }
+
+    mbedtls_printf("One-shot hash operation successful!\n\n");
+
+    /* Print out result */
+    mbedtls_printf("The SHA-256( '%s' ) is: ", sample_message);
+
+    for (size_t j = 0; j < expected_hash_len; j++) {
+        mbedtls_printf("%02x", hash[j]);
+    }
+
+    mbedtls_printf("\n");
+
+    mbedtls_psa_crypto_free();
+    return EXIT_SUCCESS;
+
+cleanup:
+    return EXIT_FAILURE;
+}
+#endif /* !MBEDTLS_PSA_CRYPTO_C || !PSA_WANT_ALG_SHA_256 */
diff --git a/tests/psa-client-server/psasim/src/client.c b/tests/psa-client-server/psasim/src/client.c
index 550a6e8..a8c9e08 100644
--- a/tests/psa-client-server/psasim/src/client.c
+++ b/tests/psa-client-server/psasim/src/client.c
@@ -5,50 +5,17 @@
  *  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"
-
 /* Includes from mbedtls */
-#include "mbedtls/version.h"
 #include "psa/crypto.h"
 
-#define CLIENT_PRINT(fmt, ...) \
-    PRINT("Client: " fmt, ##__VA_ARGS__)
-
 int main()
 {
-    char mbedtls_version[18];
-    // psa_invec invecs[1];
-    // psa_outvec outvecs[1];
-    psa_status_t status;
-
-    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));
-    psa_handle_t h = psa_connect(PSA_SID_CRYPTO_SID, 1);
-
-    if (h < 0) {
-        CLIENT_PRINT("Couldn't connect %d", h);
-        return 1;
-    }
-
-    status = psa_call(h, PSA_CRYPTO_INIT, NULL, 0, NULL, 0);
-    CLIENT_PRINT("PSA_CRYPTO_INIT returned: %d", status);
-
-    CLIENT_PRINT("Closing handle");
-    psa_close(h);
-
+    /* psa_crypto_init() connects to the server */
+    psa_status_t status = psa_crypto_init();
     if (status != PSA_SUCCESS) {
         return 1;
     }
+
+    mbedtls_psa_crypto_free();
     return 0;
 }
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..0093733 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,25 @@
+/* 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_ABORT,
+    PSA_HASH_CLONE,
+    PSA_HASH_COMPARE,
+    PSA_HASH_COMPUTE,
+    PSA_HASH_FINISH,
+    PSA_HASH_SETUP,
+    PSA_HASH_UPDATE,
+    PSA_HASH_VERIFY,
 };
 
 #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..4ac6c4a
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c
@@ -0,0 +1,701 @@
+/* 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_abort(
+    psa_hash_operation_t *operation
+    )
+{
+    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_hash_operation_t_needs(*operation);
+
+    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_hash_operation_t(&pos, &remaining, *operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_ABORT,
+                         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_psa_hash_operation_t(&rpos, &rremain, operation);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
+
+
+psa_status_t psa_hash_clone(
+    const psa_hash_operation_t *source_operation,
+    psa_hash_operation_t *target_operation
+    )
+{
+    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_hash_operation_t_needs(*source_operation) +
+                    psasim_serialise_psa_hash_operation_t_needs(*target_operation);
+
+    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_hash_operation_t(&pos, &remaining, *source_operation);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_psa_hash_operation_t(&pos, &remaining, *target_operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_CLONE,
+                         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_psa_hash_operation_t(&rpos, &rremain, target_operation);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
+
+
+psa_status_t psa_hash_compare(
+    psa_algorithm_t alg,
+    const uint8_t *input, size_t  input_length,
+    const uint8_t *hash, 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_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_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_COMPARE,
+                         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;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
+
+
+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;
+}
+
+
+psa_status_t psa_hash_finish(
+    psa_hash_operation_t *operation,
+    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_hash_operation_t_needs(*operation) +
+                    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_hash_operation_t(&pos, &remaining, *operation);
+    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_FINISH,
+                         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_psa_hash_operation_t(&rpos, &rremain, operation);
+    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;
+}
+
+
+psa_status_t psa_hash_setup(
+    psa_hash_operation_t *operation,
+    psa_algorithm_t alg
+    )
+{
+    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_hash_operation_t_needs(*operation) +
+                    psasim_serialise_psa_algorithm_t_needs(alg);
+
+    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_hash_operation_t(&pos, &remaining, *operation);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_psa_algorithm_t(&pos, &remaining, alg);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_SETUP,
+                         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_psa_hash_operation_t(&rpos, &rremain, operation);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
+
+
+psa_status_t psa_hash_update(
+    psa_hash_operation_t *operation,
+    const uint8_t *input, size_t  input_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_hash_operation_t_needs(*operation) +
+                    psasim_serialise_buffer_needs(input, input_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_hash_operation_t(&pos, &remaining, *operation);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_buffer(&pos, &remaining, input, input_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_UPDATE,
+                         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_psa_hash_operation_t(&rpos, &rremain, operation);
+    if (!ok) {
+        goto fail;
+    }
+
+fail:
+    free(params);
+    free(result);
+
+    return status;
+}
+
+
+psa_status_t psa_hash_verify(
+    psa_hash_operation_t *operation,
+    const uint8_t *hash, 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_hash_operation_t_needs(*operation) +
+                    psasim_serialise_buffer_needs(hash, 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_hash_operation_t(&pos, &remaining, *operation);
+    if (!ok) {
+        goto fail;
+    }
+    ok = psasim_serialise_buffer(&pos, &remaining, hash, hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psa_crypto_call(PSA_HASH_VERIFY,
+                         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_psa_hash_operation_t(&rpos, &rremain, operation);
+    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..919eb84
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c
@@ -0,0 +1,834 @@
+/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */
+
+/* server implementations */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#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_abort_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_hash_operation_t operation;
+
+    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_hash_operation_t(&pos, &remaining, &operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_abort(
+        &operation
+        );
+
+    // 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_psa_hash_operation_t_needs(operation);
+
+    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_psa_hash_operation_t(&rpos, &rremain, operation);
+    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_clone_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_hash_operation_t source_operation;
+    psa_hash_operation_t target_operation;
+
+    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_hash_operation_t(&pos, &remaining, &source_operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_hash_operation_t(&pos, &remaining, &target_operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_clone(
+        &source_operation,
+        &target_operation
+        );
+
+    // 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_psa_hash_operation_t_needs(target_operation);
+
+    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_psa_hash_operation_t(&rpos, &rremain, target_operation);
+    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_compare_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_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_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_compare(
+        alg,
+        input, input_length,
+        hash, 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);
+
+    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;
+
+    free(input);
+    free(hash);
+
+    return 1;   // success
+
+fail:
+    free(result);
+
+    free(input);
+    free(hash);
+
+    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;
+
+    free(input);
+    free(hash);
+
+    return 1;   // success
+
+fail:
+    free(result);
+
+    free(input);
+    free(hash);
+
+    return 0;       // This shouldn't happen!
+}
+
+// Returns 1 for success, 0 for failure
+int psa_hash_finish_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_hash_operation_t operation;
+    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_hash_operation_t(&pos, &remaining, &operation);
+    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_finish(
+        &operation,
+        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_psa_hash_operation_t_needs(operation) +
+        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_psa_hash_operation_t(&rpos, &rremain, operation);
+    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;
+
+    free(hash);
+
+    return 1;   // success
+
+fail:
+    free(result);
+
+    free(hash);
+
+    return 0;       // This shouldn't happen!
+}
+
+// Returns 1 for success, 0 for failure
+int psa_hash_setup_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_hash_operation_t operation;
+    psa_algorithm_t alg;
+
+    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_hash_operation_t(&pos, &remaining, &operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_psa_algorithm_t(&pos, &remaining, &alg);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_setup(
+        &operation,
+        alg
+        );
+
+    // 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_psa_hash_operation_t_needs(operation);
+
+    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_psa_hash_operation_t(&rpos, &rremain, operation);
+    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_update_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_hash_operation_t operation;
+    uint8_t *input = NULL;
+    size_t input_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_hash_operation_t(&pos, &remaining, &operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_buffer(&pos, &remaining, &input, &input_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_update(
+        &operation,
+        input, input_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_psa_hash_operation_t_needs(operation);
+
+    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_psa_hash_operation_t(&rpos, &rremain, operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    *out_params = result;
+    *out_params_len = result_size;
+
+    free(input);
+
+    return 1;   // success
+
+fail:
+    free(result);
+
+    free(input);
+
+    return 0;       // This shouldn't happen!
+}
+
+// Returns 1 for success, 0 for failure
+int psa_hash_verify_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_hash_operation_t operation;
+    uint8_t *hash = NULL;
+    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_hash_operation_t(&pos, &remaining, &operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    ok = psasim_deserialise_buffer(&pos, &remaining, &hash, &hash_length);
+    if (!ok) {
+        goto fail;
+    }
+
+    // Now we call the actual target function
+
+    status = psa_hash_verify(
+        &operation,
+        hash, 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_psa_hash_operation_t_needs(operation);
+
+    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_psa_hash_operation_t(&rpos, &rremain, operation);
+    if (!ok) {
+        goto fail;
+    }
+
+    *out_params = result;
+    *out_params_len = result_size;
+
+    free(hash);
+
+    return 1;   // success
+
+fail:
+    free(result);
+
+    free(hash);
+
+    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_ABORT:
+            ok = psa_hash_abort_wrapper(in_params, in_params_len,
+                                        &out_params, &out_params_len);
+            break;
+        case PSA_HASH_CLONE:
+            ok = psa_hash_clone_wrapper(in_params, in_params_len,
+                                        &out_params, &out_params_len);
+            break;
+        case PSA_HASH_COMPARE:
+            ok = psa_hash_compare_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;
+        case PSA_HASH_FINISH:
+            ok = psa_hash_finish_wrapper(in_params, in_params_len,
+                                         &out_params, &out_params_len);
+            break;
+        case PSA_HASH_SETUP:
+            ok = psa_hash_setup_wrapper(in_params, in_params_len,
+                                        &out_params, &out_params_len);
+            break;
+        case PSA_HASH_UPDATE:
+            ok = psa_hash_update_wrapper(in_params, in_params_len,
+                                         &out_params, &out_params_len);
+            break;
+        case PSA_HASH_VERIFY:
+            ok = psa_hash_verify_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 100755
index 0000000..19c6a0b
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl
@@ -0,0 +1,1411 @@
+#!/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.
+#
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+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 */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#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};
+
+    my @buffers = ();           # We need to free() these on exit
+
+    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
+            push(@buffers, $n1);        # Add to the list to be free()d at end
+        } 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
+        }
+    }
+
+    my $free_buffers = join("", map { "    free($_);\n" } @buffers);
+    $free_buffers = "\n" . $free_buffers if length($free_buffers);
+
+    print $fh <<EOF;
+
+    *out_params = result;
+    *out_params_len = result_size;
+$free_buffers
+    return 1;   // success
+
+fail:
+    free(result);
+$free_buffers
+    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);
+
+/* XXX We put this next one in place to check we ignore static functions
+ *     when we eventually read all this from a real header file
+ */
+
+/** Return an initial value for a hash operation object.
+ */
+static psa_hash_operation_t psa_hash_operation_init(void);
+
+/* XXX Back to normal function declarations */
+
+/** Set up a multipart hash operation.
+ *
+ * The sequence of operations to calculate a hash (message digest)
+ * is as follows:
+ * -# Allocate an operation object which will be passed to all the functions
+ *    listed here.
+ * -# Initialize the operation object with one of the methods described in the
+ *    documentation for #psa_hash_operation_t, e.g. #PSA_HASH_OPERATION_INIT.
+ * -# Call psa_hash_setup() to specify the algorithm.
+ * -# Call psa_hash_update() zero, one or more times, passing a fragment
+ *    of the message each time. The hash that is calculated is the hash
+ *    of the concatenation of these messages in order.
+ * -# To calculate the hash, call psa_hash_finish().
+ *    To compare the hash with an expected value, call psa_hash_verify().
+ *
+ * If an error occurs at any step after a call to psa_hash_setup(), the
+ * operation will need to be reset by a call to psa_hash_abort(). The
+ * application may call psa_hash_abort() at any time after the operation
+ * has been initialized.
+ *
+ * After a successful call to psa_hash_setup(), the application must
+ * eventually terminate the operation. The following events terminate an
+ * operation:
+ * - A successful call to psa_hash_finish() or psa_hash_verify().
+ * - A call to psa_hash_abort().
+ *
+ * \param[in,out] operation The operation object to set up. It must have
+ *                          been initialized as per the documentation for
+ *                          #psa_hash_operation_t and not yet in use.
+ * \param alg               The hash algorithm to compute (\c PSA_ALG_XXX value
+ *                          such that #PSA_ALG_IS_HASH(\p alg) is true).
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not a supported hash algorithm.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p alg is not a hash algorithm.
+ * \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 operation state is not valid (it must be inactive), or
+ *         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_setup(psa_hash_operation_t *operation,
+                            psa_algorithm_t alg);
+
+/** Add a message fragment to a multipart hash operation.
+ *
+ * The application must call psa_hash_setup() before calling this function.
+ *
+ * If this function returns an error status, the operation enters an error
+ * state and must be aborted by calling psa_hash_abort().
+ *
+ * \param[in,out] operation Active hash operation.
+ * \param[in] input         Buffer containing the message fragment to hash.
+ * \param input_length      Size of the \p input buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \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 operation state is not valid (it must be active), or
+ *         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_update(psa_hash_operation_t *operation,
+                             const uint8_t *input,
+                             size_t input_length);
+
+/** Finish the calculation of the hash of a message.
+ *
+ * The application must call psa_hash_setup() before calling this function.
+ * This function calculates the hash of the message formed by concatenating
+ * the inputs passed to preceding calls to psa_hash_update().
+ *
+ * When this function returns successfully, the operation becomes inactive.
+ * If this function returns an error status, the operation enters an error
+ * state and must be aborted by calling psa_hash_abort().
+ *
+ * \warning Applications should not call this function if they expect
+ *          a specific value for the hash. Call psa_hash_verify() instead.
+ *          Beware that comparing integrity or authenticity data such as
+ *          hash values with a function such as \c memcmp is risky
+ *          because the time taken by the comparison may leak information
+ *          about the hashed data which could allow an attacker to guess
+ *          a valid hash and thereby bypass security controls.
+ *
+ * \param[in,out] operation     Active hash operation.
+ * \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(\c alg) where \c alg is the
+ *                              hash algorithm that is calculated.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p hash buffer is too small. You can determine a
+ *         sufficient buffer size by calling #PSA_HASH_LENGTH(\c alg)
+ *         where \c alg is the hash algorithm that is calculated.
+ * \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 operation state is not valid (it must be active), or
+ *         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_finish(psa_hash_operation_t *operation,
+                             uint8_t *hash,
+                             size_t hash_size,
+                             size_t *hash_length);
+
+/** Finish the calculation of the hash of a message and compare it with
+ * an expected value.
+ *
+ * The application must call psa_hash_setup() before calling this function.
+ * This function calculates the hash of the message formed by concatenating
+ * the inputs passed to preceding calls to psa_hash_update(). It then
+ * compares the calculated hash with the expected hash passed as a
+ * parameter to this function.
+ *
+ * When this function returns successfully, the operation becomes inactive.
+ * If this function returns an error status, the operation enters an error
+ * state and must be aborted by calling psa_hash_abort().
+ *
+ * \note Implementations shall make the best effort to ensure that the
+ * comparison between the actual hash and the expected hash is performed
+ * in constant time.
+ *
+ * \param[in,out] operation     Active hash operation.
+ * \param[in] hash              Buffer containing the expected hash value.
+ * \param hash_length           Size of the \p hash buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The expected hash is identical to the actual hash of the message.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The hash of the message was calculated successfully, but it
+ *         differs from the expected hash.
+ * \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 operation state is not valid (it must be active), or
+ *         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_verify(psa_hash_operation_t *operation,
+                             const uint8_t *hash,
+                             size_t hash_length);
+
+/** Abort a hash operation.
+ *
+ * Aborting an operation frees all associated resources except for the
+ * \p operation structure itself. Once aborted, the operation object
+ * can be reused for another operation by calling
+ * psa_hash_setup() again.
+ *
+ * You may call this function any time after the operation object has
+ * been initialized by one of the methods described in #psa_hash_operation_t.
+ *
+ * In particular, calling psa_hash_abort() after the operation has been
+ * terminated by a call to psa_hash_abort(), psa_hash_finish() or
+ * psa_hash_verify() is safe and has no effect.
+ *
+ * \param[in,out] operation     Initialized hash operation.
+ *
+ * \retval #PSA_SUCCESS \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_abort(psa_hash_operation_t *operation);
+
+/** Clone a hash operation.
+ *
+ * This function copies the state of an ongoing hash operation to
+ * a new operation object. In other words, this function is equivalent
+ * to calling psa_hash_setup() on \p target_operation with the same
+ * algorithm that \p source_operation was set up for, then
+ * psa_hash_update() on \p target_operation with the same input that
+ * that was passed to \p source_operation. After this function returns, the
+ * two objects are independent, i.e. subsequent calls involving one of
+ * the objects do not affect the other object.
+ *
+ * \param[in] source_operation      The active hash operation to clone.
+ * \param[in,out] target_operation  The operation object to set up.
+ *                                  It must be initialized but not active.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription
+ * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The \p source_operation state is not valid (it must be active), or
+ *         the \p target_operation state is not valid (it must be inactive), or
+ *         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_clone(const psa_hash_operation_t *source_operation,
+                            psa_hash_operation_t *target_operation);
+
+/** Calculate the hash (digest) of a message and compare it with a
+ * reference value.
+ *
+ * \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 containing the expected hash value.
+ * \param hash_length       Size of the \p hash buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ *         The expected hash is identical to the actual hash of the input.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The hash of the message was calculated successfully, but it
+ *         differs from the expected hash.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ *         \p alg is not supported or is not a hash algorithm.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         \p input_length or \p hash_length do not match the hash size for \p alg
+ * \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_compare(psa_algorithm_t alg,
+                              const uint8_t *input,
+                              size_t input_length,
+                              const uint8_t *hash,
+                              size_t hash_length);
diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.c b/tests/psa-client-server/psasim/src/psa_sim_serialise.c
new file mode 100644
index 0000000..78ae9d6
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.c
@@ -0,0 +1,406 @@
+/**
+ * \file psa_sim_serialise.c
+ *
+ * \brief Rough-and-ready serialisation and deserialisation for the PSA Crypto simulator
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "psa_sim_serialise.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Basic idea:
+ *
+ * All arguments to a function will be serialised into a single buffer to
+ * be sent to the server with the PSA crypto function to be called.
+ *
+ * All returned data (the function's return value and any values returned
+ * via `out` parameters) will similarly be serialised into a buffer to be
+ * sent back to the client from the server.
+ *
+ * For each data type foo (e.g. int, size_t, psa_algorithm_t, but also "buffer"
+ * where "buffer" is a (uint8_t *, size_t) pair, we have a pair of functions,
+ * psasim_serialise_foo() and psasim_deserialise_foo().
+ *
+ * We also have psasim_serialise_foo_needs() functions, which return a
+ * size_t giving the number of bytes that serialising that instance of that
+ * type will need. This allows callers to size buffers for serialisation.
+ *
+ * Each serialised buffer starts with a version byte, bytes that indicate
+ * the size of basic C types, and four bytes that indicate the endianness
+ * (to avoid incompatibilities if we ever run this over a network - we are
+ * not aiming for universality, just for correctness and simplicity).
+ *
+ * Most types are serialised as a fixed-size (per type) octet string, with
+ * no type indication. This is acceptable as (a) this is for the test PSA crypto
+ * simulator only, not production, and (b) these functions are called by
+ * code that itself is written by script.
+ *
+ * We also want to keep serialised data reasonably compact as communication
+ * between client and server goes in messages of less than 200 bytes each.
+ *
+ * Many serialisation functions can be created by a script; an exemplar Perl
+ * script is included. It is not hooked into the build and so must be run
+ * manually, but is expected to be replaced by a Python script in due course.
+ * Types that can have their functions created by script include plain old C
+ * data types (e.g. int), types typedef'd to those, and even structures that
+ * don't contain pointers.
+ */
+
+size_t psasim_serialise_begin_needs(void)
+{
+    /* The serialisation buffer will
+     * start with a byte of 0 to indicate version 0,
+     * then have 1 byte each for length of int, long, void *,
+     * then have 4 bytes to indicate endianness. */
+    return 4 + sizeof(uint32_t);
+}
+
+int psasim_serialise_begin(uint8_t **pos, size_t *remaining)
+{
+    uint32_t endian = 0x1234;
+
+    if (*remaining < 4 + sizeof(endian)) {
+        return 0;
+    }
+
+    *(*pos)++ = 0;      /* version */
+    *(*pos)++ = (uint8_t) sizeof(int);
+    *(*pos)++ = (uint8_t) sizeof(long);
+    *(*pos)++ = (uint8_t) sizeof(void *);
+
+    memcpy(*pos, &endian, sizeof(endian));
+
+    *pos += sizeof(endian);
+
+    return 1;
+}
+
+int psasim_deserialise_begin(uint8_t **pos, size_t *remaining)
+{
+    uint8_t version = 255;
+    uint8_t int_size = 0;
+    uint8_t long_size = 0;
+    uint8_t ptr_size = 0;
+    uint32_t endian;
+
+    if (*remaining < 4 + sizeof(endian)) {
+        return 0;
+    }
+
+    memcpy(&version, (*pos)++, sizeof(version));
+    if (version != 0) {
+        return 0;
+    }
+
+    memcpy(&int_size, (*pos)++, sizeof(int_size));
+    if (int_size != sizeof(int)) {
+        return 0;
+    }
+
+    memcpy(&long_size, (*pos)++, sizeof(long_size));
+    if (long_size != sizeof(long)) {
+        return 0;
+    }
+
+    memcpy(&ptr_size, (*pos)++, sizeof(ptr_size));
+    if (ptr_size != sizeof(void *)) {
+        return 0;
+    }
+
+    *remaining -= 4;
+
+    memcpy(&endian, *pos, sizeof(endian));
+    if (endian != 0x1234) {
+        return 0;
+    }
+
+    *pos += sizeof(endian);
+    *remaining -= sizeof(endian);
+
+    return 1;
+}
+
+size_t psasim_serialise_unsigned_int_needs(unsigned int value)
+{
+    return sizeof(value);
+}
+
+int psasim_serialise_unsigned_int(uint8_t **pos,
+                                  size_t *remaining,
+                                  unsigned int value)
+{
+    if (*remaining < sizeof(value)) {
+        return 0;
+    }
+
+    memcpy(*pos, &value, sizeof(value));
+    *pos += sizeof(value);
+
+    return 1;
+}
+
+int psasim_deserialise_unsigned_int(uint8_t **pos,
+                                    size_t *remaining,
+                                    unsigned int *value)
+{
+    if (*remaining < sizeof(*value)) {
+        return 0;
+    }
+
+    memcpy(value, *pos, sizeof(*value));
+
+    *pos += sizeof(*value);
+    *remaining -= sizeof(*value);
+
+    return 1;
+}
+
+size_t psasim_serialise_int_needs(int value)
+{
+    return sizeof(value);
+}
+
+int psasim_serialise_int(uint8_t **pos,
+                         size_t *remaining,
+                         int value)
+{
+    if (*remaining < sizeof(value)) {
+        return 0;
+    }
+
+    memcpy(*pos, &value, sizeof(value));
+    *pos += sizeof(value);
+
+    return 1;
+}
+
+int psasim_deserialise_int(uint8_t **pos,
+                           size_t *remaining,
+                           int *value)
+{
+    if (*remaining < sizeof(*value)) {
+        return 0;
+    }
+
+    memcpy(value, *pos, sizeof(*value));
+
+    *pos += sizeof(*value);
+    *remaining -= sizeof(*value);
+
+    return 1;
+}
+
+size_t psasim_serialise_size_t_needs(size_t value)
+{
+    return sizeof(value);
+}
+
+int psasim_serialise_size_t(uint8_t **pos,
+                            size_t *remaining,
+                            size_t value)
+{
+    if (*remaining < sizeof(value)) {
+        return 0;
+    }
+
+    memcpy(*pos, &value, sizeof(value));
+    *pos += sizeof(value);
+
+    return 1;
+}
+
+int psasim_deserialise_size_t(uint8_t **pos,
+                              size_t *remaining,
+                              size_t *value)
+{
+    if (*remaining < sizeof(*value)) {
+        return 0;
+    }
+
+    memcpy(value, *pos, sizeof(*value));
+
+    *pos += sizeof(*value);
+    *remaining -= sizeof(*value);
+
+    return 1;
+}
+
+size_t psasim_serialise_buffer_needs(const uint8_t *buffer, size_t buffer_size)
+{
+    (void) buffer;
+    return sizeof(buffer_size) + buffer_size;
+}
+
+int psasim_serialise_buffer(uint8_t **pos,
+                            size_t *remaining,
+                            const uint8_t *buffer,
+                            size_t buffer_length)
+{
+    if (*remaining < sizeof(buffer_length) + buffer_length) {
+        return 0;
+    }
+
+    memcpy(*pos, &buffer_length, sizeof(buffer_length));
+    *pos += sizeof(buffer_length);
+
+    if (buffer_length > 0) {    // To be able to serialise (NULL, 0)
+        memcpy(*pos, buffer, buffer_length);
+        *pos += buffer_length;
+    }
+
+    return 1;
+}
+
+int psasim_deserialise_buffer(uint8_t **pos,
+                              size_t *remaining,
+                              uint8_t **buffer,
+                              size_t *buffer_length)
+{
+    if (*remaining < sizeof(*buffer_length)) {
+        return 0;
+    }
+
+    memcpy(buffer_length, *pos, sizeof(*buffer_length));
+
+    *pos += sizeof(buffer_length);
+    *remaining -= sizeof(buffer_length);
+
+    if (*buffer_length == 0) {          // Deserialise (NULL, 0)
+        *buffer = NULL;
+        return 1;
+    }
+
+    if (*remaining < *buffer_length) {
+        return 0;
+    }
+
+    uint8_t *data = malloc(*buffer_length);
+    if (data == NULL) {
+        return 0;
+    }
+
+    memcpy(data, *pos, *buffer_length);
+    *pos += *buffer_length;
+    *remaining -= *buffer_length;
+
+    *buffer = data;
+
+    return 1;
+}
+
+/* When the client is deserialising a buffer returned from the server, it needs
+ * to use this function to deserialised the  returned buffer. It should use the
+ * usual \c psasim_serialise_buffer() function to serialise the outbound
+ * buffer. */
+int psasim_deserialise_return_buffer(uint8_t **pos,
+                                     size_t *remaining,
+                                     uint8_t *buffer,
+                                     size_t buffer_length)
+{
+    if (*remaining < sizeof(buffer_length)) {
+        return 0;
+    }
+
+    size_t length_check;
+
+    memcpy(&length_check, *pos, sizeof(buffer_length));
+
+    *pos += sizeof(buffer_length);
+    *remaining -= sizeof(buffer_length);
+
+    if (buffer_length != length_check) {        // Make sure we're sent back the same we sent to the server
+        return 0;
+    }
+
+    if (length_check == 0) {          // Deserialise (NULL, 0)
+        return 1;
+    }
+
+    if (*remaining < buffer_length) {
+        return 0;
+    }
+
+    memcpy(buffer, *pos, buffer_length);
+    *pos += buffer_length;
+    *remaining -= buffer_length;
+
+    return 1;
+}
+
+size_t psasim_serialise_psa_status_t_needs(psa_status_t value)
+{
+    return psasim_serialise_int_needs(value);
+}
+
+int psasim_serialise_psa_status_t(uint8_t **pos,
+                                  size_t *remaining,
+                                  psa_status_t value)
+{
+    return psasim_serialise_int(pos, remaining, value);
+}
+
+int psasim_deserialise_psa_status_t(uint8_t **pos,
+                                    size_t *remaining,
+                                    psa_status_t *value)
+{
+    return psasim_deserialise_int(pos, remaining, value);
+}
+
+size_t psasim_serialise_psa_algorithm_t_needs(psa_algorithm_t value)
+{
+    return psasim_serialise_unsigned_int_needs(value);
+}
+
+int psasim_serialise_psa_algorithm_t(uint8_t **pos,
+                                     size_t *remaining,
+                                     psa_algorithm_t value)
+{
+    return psasim_serialise_unsigned_int(pos, remaining, value);
+}
+
+int psasim_deserialise_psa_algorithm_t(uint8_t **pos,
+                                       size_t *remaining,
+                                       psa_algorithm_t *value)
+{
+    return psasim_deserialise_unsigned_int(pos, remaining, value);
+}
+
+size_t psasim_serialise_psa_hash_operation_t_needs(psa_hash_operation_t value)
+{
+    return sizeof(value);
+}
+
+int psasim_serialise_psa_hash_operation_t(uint8_t **pos,
+                                          size_t *remaining,
+                                          psa_hash_operation_t value)
+{
+    if (*remaining < sizeof(value)) {
+        return 0;
+    }
+
+    memcpy(*pos, &value, sizeof(value));
+    *pos += sizeof(value);
+
+    return 1;
+}
+
+int psasim_deserialise_psa_hash_operation_t(uint8_t **pos,
+                                            size_t *remaining,
+                                            psa_hash_operation_t *value)
+{
+    if (*remaining < sizeof(*value)) {
+        return 0;
+    }
+
+    memcpy(value, *pos, sizeof(*value));
+
+    *pos += sizeof(*value);
+    *remaining -= sizeof(*value);
+
+    return 1;
+}
diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.h b/tests/psa-client-server/psasim/src/psa_sim_serialise.h
new file mode 100644
index 0000000..d5eaccf
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.h
@@ -0,0 +1,410 @@
+/**
+ * \file psa_sim_serialise.h
+ *
+ * \brief Rough-and-ready serialisation and deserialisation for the PSA Crypto simulator
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "psa/crypto.h"
+#include "psa/crypto_types.h"
+#include "psa/crypto_values.h"
+
+/* Basic idea:
+ *
+ * All arguments to a function will be serialised into a single buffer to
+ * be sent to the server with the PSA crypto function to be called.
+ *
+ * All returned data (the function's return value and any values returned
+ * via `out` parameters) will similarly be serialised into a buffer to be
+ * sent back to the client from the server.
+ *
+ * For each data type foo (e.g. int, size_t, psa_algorithm_t, but also "buffer"
+ * where "buffer" is a (uint8_t *, size_t) pair, we have a pair of functions,
+ * psasim_serialise_foo() and psasim_deserialise_foo().
+ *
+ * We also have psasim_serialise_foo_needs() functions, which return a
+ * size_t giving the number of bytes that serialising that instance of that
+ * type will need. This allows callers to size buffers for serialisation.
+ *
+ * Each serialised buffer starts with a version byte, bytes that indicate
+ * the size of basic C types, and four bytes that indicate the endianness
+ * (to avoid incompatibilities if we ever run this over a network - we are
+ * not aiming for universality, just for correctness and simplicity).
+ *
+ * Most types are serialised as a fixed-size (per type) octet string, with
+ * no type indication. This is acceptable as (a) this is for the test PSA crypto
+ * simulator only, not production, and (b) these functions are called by
+ * code that itself is written by script.
+ *
+ * We also want to keep serialised data reasonably compact as communication
+ * between client and server goes in messages of less than 200 bytes each.
+ *
+ * Many serialisation functions can be created by a script; an exemplar Perl
+ * script is included. It is not hooked into the build and so must be run
+ * manually, but is expected to be replaced by a Python script in due course.
+ * Types that can have their functions created by script include plain old C
+ * data types (e.g. int), types typedef'd to those, and even structures that
+ * don't contain pointers.
+ */
+
+/** Return how much buffer space is needed by \c psasim_serialise_begin().
+ *
+ * \return                   The number of bytes needed in the buffer for
+ *                           \c psasim_serialise_begin()'s output.
+ */
+size_t psasim_serialise_begin_needs(void);
+
+/** Begin serialisation into a buffer.
+ *
+ *                           This must be the first serialisation API called
+ *                           on a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error (likely
+ *                           no space).
+ */
+int psasim_serialise_begin(uint8_t **pos, size_t *remaining);
+
+/** Begin deserialisation of a buffer.
+ *
+ *                           This must be the first deserialisation API called
+ *                           on a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_begin(uint8_t **pos, size_t *remaining);
+
+/** Return how much buffer space is needed by \c psasim_serialise_unsigned_int()
+ *  to serialise an `unsigned int`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_unsigned_int() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_unsigned_int_needs(unsigned int value);
+
+/** Serialise an `unsigned int` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_unsigned_int(uint8_t **pos,
+                                  size_t *remaining,
+                                  unsigned int value);
+
+/** Deserialise an `unsigned int` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to an `unsigned int` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_unsigned_int(uint8_t **pos,
+                                    size_t *remaining,
+                                    unsigned int *value);
+
+/** Return how much buffer space is needed by \c psasim_serialise_int()
+ *  to serialise an `int`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_int() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_int_needs(int value);
+
+/** Serialise an `int` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_int(uint8_t **pos,
+                         size_t *remaining,
+                         int value);
+
+/** Deserialise an `int` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to an `int` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_int(uint8_t **pos,
+                           size_t *remaining,
+                           int *value);
+
+/** Return how much buffer space is needed by \c psasim_serialise_size_t()
+ *  to serialise a `size_t`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_size_t() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_size_t_needs(size_t value);
+
+/** Serialise a `size_t` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_size_t(uint8_t **pos,
+                            size_t *remaining,
+                            size_t value);
+
+/** Deserialise a `size_t` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to a `size_t` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_size_t(uint8_t **pos,
+                              size_t *remaining,
+                              size_t *value);
+
+/** Return how much space is needed by \c psasim_serialise_buffer()
+ *  to serialise a buffer: a (`uint8_t *`, `size_t`) pair.
+ *
+ * \param buffer             Pointer to the buffer to be serialised
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ * \param buffer_size        Number of bytes in the buffer to be serialised.
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_buffer() to serialise
+ *                           the specified buffer.
+ */
+size_t psasim_serialise_buffer_needs(const uint8_t *buffer, size_t buffer_size);
+
+/** Serialise a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param buffer             Pointer to the buffer to be serialised.
+ * \param buffer_length      Number of bytes in the buffer to be serialised.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_buffer(uint8_t **pos, size_t *remaining,
+                            const uint8_t *buffer, size_t buffer_length);
+
+/** Deserialise a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the serialisation buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the serialisation buffer.
+ * \param buffer             Pointer to a `uint8_t *` to receive the address
+ *                           of a newly-allocated buffer, which the caller
+ *                           must `free()`.
+ * \param buffer_length      Pointer to a `size_t` to receive the number of
+ *                           bytes in the deserialised buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_buffer(uint8_t **pos, size_t *remaining,
+                              uint8_t **buffer, size_t *buffer_length);
+
+/** Deserialise a buffer returned from the server.
+ *
+ * When the client is deserialising a buffer returned from the server, it needs
+ * to use this function to deserialised the  returned buffer. It should use the
+ * usual \c psasim_serialise_buffer() function to serialise the outbound
+ * buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the serialisation buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the serialisation buffer.
+ * \param buffer             Pointer to a `uint8_t *` to receive the address
+ *                           of a newly-allocated buffer, which the caller
+ *                           must `free()`.
+ * \param buffer_length      Pointer to a `size_t` to receive the number of
+ *                           bytes in the deserialised buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_return_buffer(uint8_t **pos, size_t *remaining,
+                                     uint8_t *buffer, size_t buffer_length);
+
+/** Return how much buffer space is needed by \c psasim_serialise_psa_status_t()
+ *  to serialise a `psa_status_t`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_psa_status_t() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_psa_status_t_needs(psa_status_t value);
+
+/** Serialise a `psa_status_t` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_psa_status_t(uint8_t **pos,
+                                  size_t *remaining,
+                                  psa_status_t value);
+
+/** Deserialise a `psa_status_t` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to a `psa_status_t` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_psa_status_t(uint8_t **pos,
+                                    size_t *remaining,
+                                    psa_status_t *value);
+
+/** Return how much buffer space is needed by \c psasim_serialise_psa_algorithm_t()
+ *  to serialise a `psa_algorithm_t`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_psa_algorithm_t() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_psa_algorithm_t_needs(psa_algorithm_t value);
+
+/** Serialise a `psa_algorithm_t` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_psa_algorithm_t(uint8_t **pos,
+                                     size_t *remaining,
+                                     psa_algorithm_t value);
+
+/** Deserialise a `psa_algorithm_t` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to a `psa_algorithm_t` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_psa_algorithm_t(uint8_t **pos,
+                                       size_t *remaining,
+                                       psa_algorithm_t *value);
+
+/** Return how much buffer space is needed by \c psasim_serialise_psa_hash_operation_t()
+ *  to serialise a `psa_hash_operation_t`.
+ *
+ * \param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_psa_hash_operation_t() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_psa_hash_operation_t_needs(psa_hash_operation_t value);
+
+/** Serialise a `psa_hash_operation_t` into a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              The value to serialise into the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_psa_hash_operation_t(uint8_t **pos,
+                                          size_t *remaining,
+                                          psa_hash_operation_t value);
+
+/** Deserialise a `psa_hash_operation_t` from a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param value              Pointer to a `psa_hash_operation_t` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_psa_hash_operation_t(uint8_t **pos,
+                                            size_t *remaining,
+                                            psa_hash_operation_t *value);
diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl
new file mode 100755
index 0000000..5161db1
--- /dev/null
+++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl
@@ -0,0 +1,747 @@
+#!/usr/bin/env perl
+#
+# psa_sim_serialise.pl - Sample Perl script to show how many serialisation
+#                        functions can be created by templated scripting.
+#
+# This is an example only, and is expected to be replaced by a Python script
+# for production use. It is not hooked into the build: it needs to be run
+# manually:
+#
+# perl psa_sim_serialise.pl h > psa_sim_serialise.h
+# perl psa_sim_serialise.pl c > psa_sim_serialise.c
+#
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+use strict;
+
+my $usage = "$0: usage: $0 c|h\n";
+my $which = lc(shift) || die($usage);
+die($usage) unless $which eq "c" || $which eq "h";
+
+# Most types are serialised as a fixed-size (per type) octet string, with
+# no type indication. This is acceptable as (a) this is for the test PSA crypto
+# simulator only, not production, and (b) these functions are called by
+# code that itself is written by script.
+#
+# We also want to keep serialised data reasonably compact as communication
+# between client and server goes in messages of less than 200 bytes each.
+#
+# This script is able to create serialisation functions for plain old C data
+# types (e.g. unsigned int), types typedef'd to those, and even structures
+# that don't contain pointers.
+#
+# Structures that contain pointers will need to have their serialisation and
+# deserialisation functions written manually (like those for the "buffer" type
+# are).
+#
+my @types = qw(unsigned-int int size_t
+               buffer
+               psa_status_t psa_algorithm_t
+               psa_hash_operation_t);
+grep(s/-/ /g, @types);
+
+# IS-A: Some data types are typedef'd; we serialise them as the other type
+my %isa = (
+    "psa_status_t" => "int",
+    "psa_algorithm_t" => "unsigned int",
+);
+
+if ($which eq "h") {
+
+    print h_header();
+
+    for my $type (@types) {
+        if ($type eq "buffer") {
+            print declare_buffer_functions();
+        } else {
+            print declare_needs($type);
+            print declare_serialise($type);
+            print declare_deserialise($type);
+        }
+    }
+
+} elsif ($which eq "c") {
+
+    print c_header();
+
+    for my $type (@types) {
+        if ($type eq "buffer") {
+            print define_buffer_functions();
+        } elsif (exists($isa{$type})) {
+            print define_needs_isa($type, $isa{$type});
+            print define_serialise_isa($type, $isa{$type});
+            print define_deserialise_isa($type, $isa{$type});
+        } else {
+            print define_needs($type);
+            print define_serialise($type);
+            print define_deserialise($type);
+        }
+    }
+
+} else {
+    die("internal error - shouldn't happen");
+}
+
+sub declare_needs
+{
+    my ($type) = @_;
+
+    my $an = ($type =~ /^[ui]/) ? "an" : "a";
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return <<EOF;
+
+/** Return how much buffer space is needed by \\c psasim_serialise_$type_d()
+ *  to serialise $an `$type`.
+ *
+ * \\param value              The value that will be serialised into the buffer
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ *
+ * \\return                   The number of bytes needed in the buffer by
+ *                           \\c psasim_serialise_$type_d() to serialise
+ *                           the given value.
+ */
+size_t psasim_serialise_${type_d}_needs($type value);
+EOF
+}
+
+sub declare_serialise
+{
+    my ($type) = @_;
+
+    my $an = ($type =~ /^[ui]/) ? "an" : "a";
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return align_declaration(<<EOF);
+
+/** Serialise $an `$type` into a buffer.
+ *
+ * \\param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \\param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \\param value              The value to serialise into the buffer.
+ *
+ * \\return                   \\c 1 on success ("okay"), \\c 0 on error.
+ */
+int psasim_serialise_$type_d(uint8_t **pos,
+                             size_t *remaining,
+                             $type value);
+EOF
+}
+
+sub declare_deserialise
+{
+    my ($type) = @_;
+
+    my $an = ($type =~ /^[ui]/) ? "an" : "a";
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return align_declaration(<<EOF);
+
+/** Deserialise $an `$type` from a buffer.
+ *
+ * \\param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \\param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \\param value              Pointer to $an `$type` to receive the value
+ *                           deserialised from the buffer.
+ *
+ * \\return                   \\c 1 on success ("okay"), \\c 0 on error.
+ */
+int psasim_deserialise_$type_d(uint8_t **pos,
+                               size_t *remaining,
+                               $type *value);
+EOF
+}
+
+sub declare_buffer_functions
+{
+    return <<'EOF';
+
+/** Return how much space is needed by \c psasim_serialise_buffer()
+ *  to serialise a buffer: a (`uint8_t *`, `size_t`) pair.
+ *
+ * \param buffer             Pointer to the buffer to be serialised
+ *                           (needed in case some serialisations are value-
+ *                           dependent).
+ * \param buffer_size        Number of bytes in the buffer to be serialised.
+ *
+ * \return                   The number of bytes needed in the buffer by
+ *                           \c psasim_serialise_buffer() to serialise
+ *                           the specified buffer.
+ */
+size_t psasim_serialise_buffer_needs(const uint8_t *buffer, size_t buffer_size);
+
+/** Serialise a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ * \param buffer             Pointer to the buffer to be serialised.
+ * \param buffer_length      Number of bytes in the buffer to be serialised.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_serialise_buffer(uint8_t **pos, size_t *remaining,
+                            const uint8_t *buffer, size_t buffer_length);
+
+/** Deserialise a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the serialisation buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the serialisation buffer.
+ * \param buffer             Pointer to a `uint8_t *` to receive the address
+ *                           of a newly-allocated buffer, which the caller
+ *                           must `free()`.
+ * \param buffer_length      Pointer to a `size_t` to receive the number of
+ *                           bytes in the deserialised buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_buffer(uint8_t **pos, size_t *remaining,
+                              uint8_t **buffer, size_t *buffer_length);
+
+/** Deserialise a buffer returned from the server.
+ *
+ * When the client is deserialising a buffer returned from the server, it needs
+ * to use this function to deserialised the  returned buffer. It should use the
+ * usual \c psasim_serialise_buffer() function to serialise the outbound
+ * buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the serialisation buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the serialisation buffer.
+ * \param buffer             Pointer to a `uint8_t *` to receive the address
+ *                           of a newly-allocated buffer, which the caller
+ *                           must `free()`.
+ * \param buffer_length      Pointer to a `size_t` to receive the number of
+ *                           bytes in the deserialised buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_return_buffer(uint8_t **pos, size_t *remaining,
+                                     uint8_t *buffer, size_t buffer_length);
+EOF
+}
+
+sub h_header
+{
+    return <<'EOF';
+/**
+ * \file psa_sim_serialise.h
+ *
+ * \brief Rough-and-ready serialisation and deserialisation for the PSA Crypto simulator
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "psa/crypto.h"
+#include "psa/crypto_types.h"
+#include "psa/crypto_values.h"
+
+/* Basic idea:
+ *
+ * All arguments to a function will be serialised into a single buffer to
+ * be sent to the server with the PSA crypto function to be called.
+ *
+ * All returned data (the function's return value and any values returned
+ * via `out` parameters) will similarly be serialised into a buffer to be
+ * sent back to the client from the server.
+ *
+ * For each data type foo (e.g. int, size_t, psa_algorithm_t, but also "buffer"
+ * where "buffer" is a (uint8_t *, size_t) pair, we have a pair of functions,
+ * psasim_serialise_foo() and psasim_deserialise_foo().
+ *
+ * We also have psasim_serialise_foo_needs() functions, which return a
+ * size_t giving the number of bytes that serialising that instance of that
+ * type will need. This allows callers to size buffers for serialisation.
+ *
+ * Each serialised buffer starts with a version byte, bytes that indicate
+ * the size of basic C types, and four bytes that indicate the endianness
+ * (to avoid incompatibilities if we ever run this over a network - we are
+ * not aiming for universality, just for correctness and simplicity).
+ *
+ * Most types are serialised as a fixed-size (per type) octet string, with
+ * no type indication. This is acceptable as (a) this is for the test PSA crypto
+ * simulator only, not production, and (b) these functions are called by
+ * code that itself is written by script.
+ *
+ * We also want to keep serialised data reasonably compact as communication
+ * between client and server goes in messages of less than 200 bytes each.
+ *
+ * Many serialisation functions can be created by a script; an exemplar Perl
+ * script is included. It is not hooked into the build and so must be run
+ * manually, but is expected to be replaced by a Python script in due course.
+ * Types that can have their functions created by script include plain old C
+ * data types (e.g. int), types typedef'd to those, and even structures that
+ * don't contain pointers.
+ */
+
+/** Return how much buffer space is needed by \c psasim_serialise_begin().
+ *
+ * \return                   The number of bytes needed in the buffer for
+ *                           \c psasim_serialise_begin()'s output.
+ */
+size_t psasim_serialise_begin_needs(void);
+
+/** Begin serialisation into a buffer.
+ *
+ *                           This must be the first serialisation API called
+ *                           on a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error (likely
+ *                           no space).
+ */
+int psasim_serialise_begin(uint8_t **pos, size_t *remaining);
+
+/** Begin deserialisation of a buffer.
+ *
+ *                           This must be the first deserialisation API called
+ *                           on a buffer.
+ *
+ * \param pos[in,out]        Pointer to a `uint8_t *` holding current position
+ *                           in the buffer.
+ * \param remaining[in,out]  Pointer to a `size_t` holding number of bytes
+ *                           remaining in the buffer.
+ *
+ * \return                   \c 1 on success ("okay"), \c 0 on error.
+ */
+int psasim_deserialise_begin(uint8_t **pos, size_t *remaining);
+EOF
+}
+
+sub define_needs
+{
+    my ($type) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return <<EOF;
+
+size_t psasim_serialise_${type_d}_needs($type value)
+{
+    return sizeof(value);
+}
+EOF
+}
+
+sub define_needs_isa
+{
+    my ($type, $isa) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    my $isa_d = $isa;
+    $isa_d =~ s/ /_/g;
+
+    return <<EOF;
+
+size_t psasim_serialise_${type_d}_needs($type value)
+{
+    return psasim_serialise_${isa_d}_needs(value);
+}
+EOF
+}
+
+sub define_serialise
+{
+    my ($type) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return align_signature(<<EOF);
+
+int psasim_serialise_$type_d(uint8_t **pos,
+                             size_t *remaining,
+                             $type value)
+{
+    if (*remaining < sizeof(value)) {
+        return 0;
+    }
+
+    memcpy(*pos, &value, sizeof(value));
+    *pos += sizeof(value);
+
+    return 1;
+}
+EOF
+}
+
+sub define_serialise_isa
+{
+    my ($type, $isa) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    my $isa_d = $isa;
+    $isa_d =~ s/ /_/g;
+
+    return align_signature(<<EOF);
+
+int psasim_serialise_$type_d(uint8_t **pos,
+                             size_t *remaining,
+                             $type value)
+{
+    return psasim_serialise_$isa_d(pos, remaining, value);
+}
+EOF
+}
+
+sub define_deserialise
+{
+    my ($type) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    return align_signature(<<EOF);
+
+int psasim_deserialise_$type_d(uint8_t **pos,
+                               size_t *remaining,
+                               $type *value)
+{
+    if (*remaining < sizeof(*value)) {
+        return 0;
+    }
+
+    memcpy(value, *pos, sizeof(*value));
+
+    *pos += sizeof(*value);
+    *remaining -= sizeof(*value);
+
+    return 1;
+}
+EOF
+}
+
+sub define_deserialise_isa
+{
+    my ($type, $isa) = @_;
+
+    my $type_d = $type;
+    $type_d =~ s/ /_/g;
+
+    my $isa_d = $isa;
+    $isa_d =~ s/ /_/g;
+
+    return align_signature(<<EOF);
+
+int psasim_deserialise_$type_d(uint8_t **pos,
+                              size_t *remaining,
+                              $type *value)
+{
+    return psasim_deserialise_$isa_d(pos, remaining, value);
+}
+EOF
+}
+
+sub define_buffer_functions
+{
+    return <<'EOF';
+
+size_t psasim_serialise_buffer_needs(const uint8_t *buffer, size_t buffer_size)
+{
+    (void) buffer;
+    return sizeof(buffer_size) + buffer_size;
+}
+
+int psasim_serialise_buffer(uint8_t **pos,
+                            size_t *remaining,
+                            const uint8_t *buffer,
+                            size_t buffer_length)
+{
+    if (*remaining < sizeof(buffer_length) + buffer_length) {
+        return 0;
+    }
+
+    memcpy(*pos, &buffer_length, sizeof(buffer_length));
+    *pos += sizeof(buffer_length);
+
+    if (buffer_length > 0) {    // To be able to serialise (NULL, 0)
+        memcpy(*pos, buffer, buffer_length);
+        *pos += buffer_length;
+    }
+
+    return 1;
+}
+
+int psasim_deserialise_buffer(uint8_t **pos,
+                              size_t *remaining,
+                              uint8_t **buffer,
+                              size_t *buffer_length)
+{
+    if (*remaining < sizeof(*buffer_length)) {
+        return 0;
+    }
+
+    memcpy(buffer_length, *pos, sizeof(*buffer_length));
+
+    *pos += sizeof(buffer_length);
+    *remaining -= sizeof(buffer_length);
+
+    if (*buffer_length == 0) {          // Deserialise (NULL, 0)
+        *buffer = NULL;
+        return 1;
+    }
+
+    if (*remaining < *buffer_length) {
+        return 0;
+    }
+
+    uint8_t *data = malloc(*buffer_length);
+    if (data == NULL) {
+        return 0;
+    }
+
+    memcpy(data, *pos, *buffer_length);
+    *pos += *buffer_length;
+    *remaining -= *buffer_length;
+
+    *buffer = data;
+
+    return 1;
+}
+
+/* When the client is deserialising a buffer returned from the server, it needs
+ * to use this function to deserialised the  returned buffer. It should use the
+ * usual \c psasim_serialise_buffer() function to serialise the outbound
+ * buffer. */
+int psasim_deserialise_return_buffer(uint8_t **pos,
+                                     size_t *remaining,
+                                     uint8_t *buffer,
+                                     size_t buffer_length)
+{
+    if (*remaining < sizeof(buffer_length)) {
+        return 0;
+    }
+
+    size_t length_check;
+
+    memcpy(&length_check, *pos, sizeof(buffer_length));
+
+    *pos += sizeof(buffer_length);
+    *remaining -= sizeof(buffer_length);
+
+    if (buffer_length != length_check) {        // Make sure we're sent back the same we sent to the server
+        return 0;
+    }
+
+    if (length_check == 0) {          // Deserialise (NULL, 0)
+        return 1;
+    }
+
+    if (*remaining < buffer_length) {
+        return 0;
+    }
+
+    memcpy(buffer, *pos, buffer_length);
+    *pos += buffer_length;
+    *remaining -= buffer_length;
+
+    return 1;
+}
+EOF
+}
+
+sub c_header
+{
+    return <<'EOF';
+/**
+ * \file psa_sim_serialise.c
+ *
+ * \brief Rough-and-ready serialisation and deserialisation for the PSA Crypto simulator
+ */
+
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "psa_sim_serialise.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Basic idea:
+ *
+ * All arguments to a function will be serialised into a single buffer to
+ * be sent to the server with the PSA crypto function to be called.
+ *
+ * All returned data (the function's return value and any values returned
+ * via `out` parameters) will similarly be serialised into a buffer to be
+ * sent back to the client from the server.
+ *
+ * For each data type foo (e.g. int, size_t, psa_algorithm_t, but also "buffer"
+ * where "buffer" is a (uint8_t *, size_t) pair, we have a pair of functions,
+ * psasim_serialise_foo() and psasim_deserialise_foo().
+ *
+ * We also have psasim_serialise_foo_needs() functions, which return a
+ * size_t giving the number of bytes that serialising that instance of that
+ * type will need. This allows callers to size buffers for serialisation.
+ *
+ * Each serialised buffer starts with a version byte, bytes that indicate
+ * the size of basic C types, and four bytes that indicate the endianness
+ * (to avoid incompatibilities if we ever run this over a network - we are
+ * not aiming for universality, just for correctness and simplicity).
+ *
+ * Most types are serialised as a fixed-size (per type) octet string, with
+ * no type indication. This is acceptable as (a) this is for the test PSA crypto
+ * simulator only, not production, and (b) these functions are called by
+ * code that itself is written by script.
+ *
+ * We also want to keep serialised data reasonably compact as communication
+ * between client and server goes in messages of less than 200 bytes each.
+ *
+ * Many serialisation functions can be created by a script; an exemplar Perl
+ * script is included. It is not hooked into the build and so must be run
+ * manually, but is expected to be replaced by a Python script in due course.
+ * Types that can have their functions created by script include plain old C
+ * data types (e.g. int), types typedef'd to those, and even structures that
+ * don't contain pointers.
+ */
+
+size_t psasim_serialise_begin_needs(void)
+{
+    /* The serialisation buffer will
+     * start with a byte of 0 to indicate version 0,
+     * then have 1 byte each for length of int, long, void *,
+     * then have 4 bytes to indicate endianness. */
+    return 4 + sizeof(uint32_t);
+}
+
+int psasim_serialise_begin(uint8_t **pos, size_t *remaining)
+{
+    uint32_t endian = 0x1234;
+
+    if (*remaining < 4 + sizeof(endian)) {
+        return 0;
+    }
+
+    *(*pos)++ = 0;      /* version */
+    *(*pos)++ = (uint8_t) sizeof(int);
+    *(*pos)++ = (uint8_t) sizeof(long);
+    *(*pos)++ = (uint8_t) sizeof(void *);
+
+    memcpy(*pos, &endian, sizeof(endian));
+
+    *pos += sizeof(endian);
+
+    return 1;
+}
+
+int psasim_deserialise_begin(uint8_t **pos, size_t *remaining)
+{
+    uint8_t version = 255;
+    uint8_t int_size = 0;
+    uint8_t long_size = 0;
+    uint8_t ptr_size = 0;
+    uint32_t endian;
+
+    if (*remaining < 4 + sizeof(endian)) {
+        return 0;
+    }
+
+    memcpy(&version, (*pos)++, sizeof(version));
+    if (version != 0) {
+        return 0;
+    }
+
+    memcpy(&int_size, (*pos)++, sizeof(int_size));
+    if (int_size != sizeof(int)) {
+        return 0;
+    }
+
+    memcpy(&long_size, (*pos)++, sizeof(long_size));
+    if (long_size != sizeof(long)) {
+        return 0;
+    }
+
+    memcpy(&ptr_size, (*pos)++, sizeof(ptr_size));
+    if (ptr_size != sizeof(void *)) {
+        return 0;
+    }
+
+    *remaining -= 4;
+
+    memcpy(&endian, *pos, sizeof(endian));
+    if (endian != 0x1234) {
+        return 0;
+    }
+
+    *pos += sizeof(endian);
+    *remaining -= sizeof(endian);
+
+    return 1;
+}
+EOF
+}
+
+# Horrible way to align first, second and third lines of function signature to
+# appease uncrustify (these are the 2nd-4th lines of code, indices 1, 2 and 3)
+#
+sub align_signature
+{
+    my ($code) = @_;
+
+    my @code = split(/\n/, $code);
+
+    # Find where the ( is
+    my $idx = index($code[1], "(");
+    die("can't find (") if $idx < 0;
+
+    my $indent = " " x ($idx + 1);
+    $code[2] =~ s/^\s+/$indent/;
+    $code[3] =~ s/^\s+/$indent/;
+
+    return join("\n", @code) . "\n";
+}
+
+# Horrible way to align the function declaration to appease uncrustify
+#
+sub align_declaration
+{
+    my ($code) = @_;
+
+    my @code = split(/\n/, $code);
+
+    # Find out which lines we need to massage
+    my $i;
+    for ($i = 0; $i <= $#code; $i++) {
+        last if $code[$i] =~ /^int psasim_/;
+    }
+    die("can't find int psasim_") if $i > $#code;
+
+    # Find where the ( is
+    my $idx = index($code[$i], "(");
+    die("can't find (") if $idx < 0;
+
+    my $indent = " " x ($idx + 1);
+    $code[$i + 1] =~ s/^\s+/$indent/;
+    $code[$i + 2] =~ s/^\s+/$indent/;
+
+    return join("\n", @code) . "\n";
+}
diff --git a/tests/psa-client-server/psasim/src/server.c b/tests/psa-client-server/psasim/src/server.c
index 21b65c7..77ce269 100644
--- a/tests/psa-client-server/psasim/src/server.c
+++ b/tests/psa-client-server/psasim/src/server.c
@@ -53,6 +53,7 @@
     const int magic_num = 66;
     int client_disconnected = 0;
     char mbedtls_version[18];
+    extern psa_status_t psa_crypto_call(psa_msg_t msg);
 
     mbedtls_version_get_string_full(mbedtls_version);
     SERVER_PRINT("%s", mbedtls_version);
@@ -83,14 +84,7 @@
                         break;
                     default:
                         SERVER_PRINT("Got an IPC call of type %d", msg.type);
-                        switch (msg.type) {
-                            case PSA_CRYPTO_INIT:
-                                ret = psa_crypto_init();
-                                break;
-                            default:
-                                SERVER_PRINT("Unknown PSA function code");
-                                break;
-                        }
+                        ret = psa_crypto_call(msg);
                         SERVER_PRINT("Internal function call returned %d", ret);
 
                         if (msg.client_id > 0) {
diff --git a/tests/psa-client-server/psasim/test/run_test.sh b/tests/psa-client-server/psasim/test/run_test.sh
index 06bcc93..31429c8 100755
--- a/tests/psa-client-server/psasim/test/run_test.sh
+++ b/tests/psa-client-server/psasim/test/run_test.sh
@@ -30,8 +30,8 @@
 
 clean_run
 
-./psa_partition -k > psa_partition.log 2>&1 &
+./psa_partition -k &
 SERV_PID=$!
 wait_for_server_startup
-./psa_client > psa_client.log 2>&1
+./psa_client
 wait $SERV_PID
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 4f0e9bb..c0abf05 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -6228,6 +6228,30 @@
     msg "test psasim"
     tests/psa-client-server/psasim/test/run_test.sh
 
+    msg "build psasim to test psa_hash_compute"
+    # Delete the executable to ensure we build using the right MAIN
+    rm tests/psa-client-server/psasim/test/psa_client
+    # API under test: psa_hash_compute()
+    make -C tests/psa-client-server/psasim CFLAGS="$ASAN_CFLAGS" LDFLAGS="$ASAN_CFLAGS" MAIN="src/aut_psa_hash_compute.c"
+
+    msg "test psasim running psa_hash_compute"
+    tests/psa-client-server/psasim/test/run_test.sh
+
+    # Next APIs under test: psa_hash_*(). Just use the PSA hash example.
+    aut_psa_hash="../../../programs/psa/psa_hash.c"
+    if [ -f "tests/psa-client-server/psasim/$aut_psa_hash" ]; then
+
+        msg "build psasim to test all psa_hash_* APIs"
+        # Delete the executable to ensure we build using the right MAIN
+        rm tests/psa-client-server/psasim/test/psa_client
+        make -C tests/psa-client-server/psasim CFLAGS="$ASAN_CFLAGS" LDFLAGS="$ASAN_CFLAGS" MAIN="$aut_psa_hash"
+
+        msg "test psasim running psa_hash sample"
+        tests/psa-client-server/psasim/test/run_test.sh
+    else
+        echo $aut_psa_hash NOT FOUND, so not running that test
+    fi
+
     msg "clean psasim"
     make -C tests/psa-client-server/psasim clean
 }