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

Test psa_hash_xxx() APIs in the PSA crypto simulator
This commit is contained in:
Tom Cosgrove 2024-05-31 11:13:55 +00:00 committed by GitHub
commit 30a9b6f2fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 4679 additions and 51 deletions

View File

@ -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 @@ GENERATED_H_FILES = include/psa_manifest/manifest.h \
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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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);

View File

@ -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";
}

View File

@ -53,6 +53,7 @@ int psa_server_main(int argc, char *argv[])
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 @@ int psa_server_main(int argc, char *argv[])
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) {

View File

@ -30,8 +30,8 @@ function wait_for_server_startup() {
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

View File

@ -6228,6 +6228,30 @@ component_test_psasim() {
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
}