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
}