From 2fc5687b21cfae75acd754ef115d661f4893f9e2 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:03:06 +0100 Subject: [PATCH 01/11] Add PSA crypto sim serialisation functions for basic types Not hooked into the build yet Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_sim_serialise.c | 321 +++++++++ .../psasim/src/psa_sim_serialise.h | 272 +++++++ .../psasim/src/psa_sim_serialise.pl | 682 ++++++++++++++++++ 3 files changed, 1275 insertions(+) create mode 100644 tests/psa-client-server/psasim/src/psa_sim_serialise.c create mode 100644 tests/psa-client-server/psasim/src/psa_sim_serialise.h create mode 100644 tests/psa-client-server/psasim/src/psa_sim_serialise.pl 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 0000000000..7caf4e595e --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.c @@ -0,0 +1,321 @@ +/** + * \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 +#include + +/* 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; +} 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 0000000000..4ae0253f4a --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.h @@ -0,0 +1,272 @@ +/** + * \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 +#include + +#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); 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 100644 index 0000000000..092a448ba7 --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl @@ -0,0 +1,682 @@ +#!/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 +# +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); +grep(s/-/ /g, @types); + +# IS-A: Some data types are typedef'd; we serialise them as the other type +my %isa = ( + # e.g. "psa_status_t" => "int", but nothing for now +); + +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 < +#include + +#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 < 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 +#include + +/* 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 +} From e68fb72d8c7dc67a4c9fac9e79b7d70c90eb8a16 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:04:14 +0100 Subject: [PATCH 02/11] Add PSA crypto sim serialisation functions for rest of types needed for psa_hash_compute() Still not used Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_sim_serialise.c | 30 ++++++++ .../psasim/src/psa_sim_serialise.h | 76 +++++++++++++++++++ .../psasim/src/psa_sim_serialise.pl | 5 +- 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.c b/tests/psa-client-server/psasim/src/psa_sim_serialise.c index 7caf4e595e..264509cdf1 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.c +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.c @@ -319,3 +319,33 @@ int psasim_deserialise_return_buffer(uint8_t **pos, 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); +} diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.h b/tests/psa-client-server/psasim/src/psa_sim_serialise.h index 4ae0253f4a..9cca7d8c65 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.h +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.h @@ -270,3 +270,79 @@ int psasim_deserialise_buffer(uint8_t **pos, size_t *remaining, */ 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); diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl index 092a448ba7..2a6c3885b7 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl @@ -32,12 +32,13 @@ die($usage) unless $which eq "c" || $which eq "h"; # deserialisation functions written manually (like those for the "buffer" type # are). # -my @types = qw(unsigned-int int size_t buffer); +my @types = qw(unsigned-int int size_t buffer psa_status_t psa_algorithm_t); grep(s/-/ /g, @types); # IS-A: Some data types are typedef'd; we serialise them as the other type my %isa = ( - # e.g. "psa_status_t" => "int", but nothing for now + "psa_status_t" => "int", + "psa_algorithm_t" => "unsigned int", ); if ($which eq "h") { From 54b4ccdbf88e04158f63330e0049c388f1b9a6ff Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:27:01 +0100 Subject: [PATCH 03/11] Add PSA crypto sim client and server implementations of psa_hash_compute() A Perl script that creates them is also included as reference. This is not the final script (that will be in Python) but a proof-of-concept to show that creaation client and server wrappers can be scripted. It is not hooked into the build: it must be run manually. It is not part of the deliverables for this PR. Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_functions_codes.h | 13 +- .../psasim/src/psa_sim_crypto_client.c | 207 +++ .../psasim/src/psa_sim_crypto_server.c | 225 ++++ .../psasim/src/psa_sim_generate.pl | 1130 +++++++++++++++++ 4 files changed, 1573 insertions(+), 2 deletions(-) create mode 100644 tests/psa-client-server/psasim/src/psa_sim_crypto_client.c create mode 100644 tests/psa-client-server/psasim/src/psa_sim_crypto_server.c create mode 100644 tests/psa-client-server/psasim/src/psa_sim_generate.pl 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 34897b91be..9306be95a1 100644 --- a/tests/psa-client-server/psasim/src/psa_functions_codes.h +++ b/tests/psa-client-server/psasim/src/psa_functions_codes.h @@ -1,9 +1,18 @@ +/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + #ifndef _PSA_FUNCTIONS_CODES_H_ #define _PSA_FUNCTIONS_CODES_H_ enum { - PSA_CRYPTO_INIT = 0x00, - /* Add other PSA functions here */ + /* Start here to avoid overlap with PSA_IPC_CONNECT, PSA_IPC_DISCONNECT + * and VERSION_REQUEST */ + PSA_CRYPTO_INIT = 100, + PSA_HASH_COMPUTE, }; #endif /* _PSA_FUNCTIONS_CODES_H_ */ diff --git a/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c new file mode 100644 index 0000000000..85c8a3c036 --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c @@ -0,0 +1,207 @@ +/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */ + +/* client calls */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include +#include + +/* Includes from psasim */ +#include +#include +#include "psa_manifest/sid.h" +#include "psa_functions_codes.h" +#include "psa_sim_serialise.h" + +/* Includes from mbedtls */ +#include "mbedtls/version.h" +#include "psa/crypto.h" + +#define CLIENT_PRINT(fmt, ...) \ + PRINT("Client: " fmt, ##__VA_ARGS__) + +static psa_handle_t handle = -1; + +int psa_crypto_call(int function, + uint8_t *in_params, size_t in_params_len, + uint8_t **out_params, size_t *out_params_len) +{ + // psa_outvec outvecs[1]; + if (handle < 0) { + fprintf(stderr, "NOT CONNECTED\n"); + exit(1); + } + + psa_invec invec; + invec.base = in_params; + invec.len = in_params_len; + + size_t max_receive = 8192; + uint8_t *receive = malloc(max_receive); + if (receive == NULL) { + fprintf(stderr, "FAILED to allocate %u bytes\n", (unsigned) max_receive); + exit(1); + } + + size_t actual_received = 0; + + psa_outvec outvecs[2]; + outvecs[0].base = &actual_received; + outvecs[0].len = sizeof(actual_received); + outvecs[1].base = receive; + outvecs[1].len = max_receive; + + psa_status_t status = psa_call(handle, function, &invec, 1, outvecs, 2); + if (status != PSA_SUCCESS) { + free(receive); + return 0; + } + + *out_params = receive; + *out_params_len = actual_received; + + return 1; // success +} + +psa_status_t psa_crypto_init(void) +{ + char mbedtls_version[18]; + uint8_t *result = NULL; + size_t result_length; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + mbedtls_version_get_string_full(mbedtls_version); + CLIENT_PRINT("%s", mbedtls_version); + + CLIENT_PRINT("My PID: %d", getpid()); + + CLIENT_PRINT("PSA version: %u", psa_version(PSA_SID_CRYPTO_SID)); + handle = psa_connect(PSA_SID_CRYPTO_SID, 1); + + if (handle < 0) { + CLIENT_PRINT("Couldn't connect %d", handle); + return PSA_ERROR_COMMUNICATION_FAILURE; + } + + int ok = psa_crypto_call(PSA_CRYPTO_INIT, NULL, 0, &result, &result_length); + CLIENT_PRINT("PSA_CRYPTO_INIT returned: %d", ok); + + if (!ok) { + goto fail; + } + + uint8_t *rpos = result; + size_t rremain = result_length; + + ok = psasim_deserialise_begin(&rpos, &rremain); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_psa_status_t(&rpos, &rremain, &status); + if (!ok) { + goto fail; + } + +fail: + free(result); + + return status; +} + +void mbedtls_psa_crypto_free(void) +{ + CLIENT_PRINT("Closing handle"); + psa_close(handle); + handle = -1; +} + + +psa_status_t psa_hash_compute( + psa_algorithm_t alg, + const uint8_t *input, size_t input_length, + uint8_t *hash, size_t hash_size, + size_t *hash_length + ) +{ + uint8_t *params = NULL; + uint8_t *result = NULL; + size_t result_length; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + size_t needed = psasim_serialise_begin_needs() + + psasim_serialise_psa_algorithm_t_needs(alg) + + psasim_serialise_buffer_needs(input, input_length) + + psasim_serialise_buffer_needs(hash, hash_size) + + psasim_serialise_size_t_needs(*hash_length); + + params = malloc(needed); + if (params == NULL) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; + goto fail; + } + + uint8_t *pos = params; + size_t remaining = needed; + int ok; + ok = psasim_serialise_begin(&pos, &remaining); + if (!ok) { + goto fail; + } + ok = psasim_serialise_psa_algorithm_t(&pos, &remaining, alg); + if (!ok) { + goto fail; + } + ok = psasim_serialise_buffer(&pos, &remaining, input, input_length); + if (!ok) { + goto fail; + } + ok = psasim_serialise_buffer(&pos, &remaining, hash, hash_size); + if (!ok) { + goto fail; + } + ok = psasim_serialise_size_t(&pos, &remaining, *hash_length); + if (!ok) { + goto fail; + } + + ok = psa_crypto_call(PSA_HASH_COMPUTE, + params, (size_t) (pos - params), &result, &result_length); + if (!ok) { + printf("XXX server call failed\n"); + goto fail; + } + + uint8_t *rpos = result; + size_t rremain = result_length; + + ok = psasim_deserialise_begin(&rpos, &rremain); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_psa_status_t(&rpos, &rremain, &status); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_return_buffer(&rpos, &rremain, hash, hash_size); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_size_t(&rpos, &rremain, hash_length); + if (!ok) { + goto fail; + } + +fail: + free(params); + free(result); + + return status; +} diff --git a/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c new file mode 100644 index 0000000000..c15b2b0c82 --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c @@ -0,0 +1,225 @@ +/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */ + +/* server implementations */ + +#include +#include + +#include + +#include "psa_functions_codes.h" +#include "psa_sim_serialise.h" + +#include "service.h" + +// Returns 1 for success, 0 for failure +int psa_crypto_init_wrapper( + uint8_t *in_params, size_t in_params_len, + uint8_t **out_params, size_t *out_params_len) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + uint8_t *result = NULL; + int ok; + + // Now we call the actual target function + + status = psa_crypto_init( + ); + + // NOTE: Should really check there is no overflow as we go along. + size_t result_size = + psasim_serialise_begin_needs() + + psasim_serialise_psa_status_t_needs(status); + + result = malloc(result_size); + if (result == NULL) { + goto fail; + } + + uint8_t *rpos = result; + size_t rremain = result_size; + + ok = psasim_serialise_begin(&rpos, &rremain); + if (!ok) { + goto fail; + } + + ok = psasim_serialise_psa_status_t(&rpos, &rremain, status); + if (!ok) { + goto fail; + } + + *out_params = result; + *out_params_len = result_size; + + return 1; // success + +fail: + free(result); + return 0; // This shouldn't happen! +} + +// Returns 1 for success, 0 for failure +int psa_hash_compute_wrapper( + uint8_t *in_params, size_t in_params_len, + uint8_t **out_params, size_t *out_params_len) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t alg; + uint8_t *input = NULL; + size_t input_length; + uint8_t *hash = NULL; + size_t hash_size; + size_t hash_length; + + uint8_t *pos = in_params; + size_t remaining = in_params_len; + uint8_t *result = NULL; + int ok; + + ok = psasim_deserialise_begin(&pos, &remaining); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_psa_algorithm_t(&pos, &remaining, &alg); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_buffer(&pos, &remaining, &input, &input_length); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_buffer(&pos, &remaining, &hash, &hash_size); + if (!ok) { + goto fail; + } + + ok = psasim_deserialise_size_t(&pos, &remaining, &hash_length); + if (!ok) { + goto fail; + } + + // Now we call the actual target function + + status = psa_hash_compute( + alg, + input, input_length, + hash, hash_size, + &hash_length + ); + + // NOTE: Should really check there is no overflow as we go along. + size_t result_size = + psasim_serialise_begin_needs() + + psasim_serialise_psa_status_t_needs(status) + + psasim_serialise_buffer_needs(hash, hash_size) + + psasim_serialise_size_t_needs(hash_length); + + result = malloc(result_size); + if (result == NULL) { + goto fail; + } + + uint8_t *rpos = result; + size_t rremain = result_size; + + ok = psasim_serialise_begin(&rpos, &rremain); + if (!ok) { + goto fail; + } + + ok = psasim_serialise_psa_status_t(&rpos, &rremain, status); + if (!ok) { + goto fail; + } + + ok = psasim_serialise_buffer(&rpos, &rremain, hash, hash_size); + if (!ok) { + goto fail; + } + + ok = psasim_serialise_size_t(&rpos, &rremain, hash_length); + if (!ok) { + goto fail; + } + + *out_params = result; + *out_params_len = result_size; + + return 1; // success + +fail: + free(result); + return 0; // This shouldn't happen! +} + +psa_status_t psa_crypto_call(psa_msg_t msg) +{ + int ok = 0; + + int func = msg.type; + + /* We only expect a single input buffer, with everything serialised in it */ + if (msg.in_size[1] != 0 || msg.in_size[2] != 0 || msg.in_size[3] != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* We expect exactly 2 output buffers, one for size, the other for data */ + if (msg.out_size[0] != sizeof(size_t) || msg.out_size[1] == 0 || + msg.out_size[2] != 0 || msg.out_size[3] != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + uint8_t *in_params = NULL; + size_t in_params_len = 0; + uint8_t *out_params = NULL; + size_t out_params_len = 0; + + in_params_len = msg.in_size[0]; + in_params = malloc(in_params_len); + if (in_params == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + /* Read the bytes from the client */ + size_t actual = psa_read(msg.handle, 0, in_params, in_params_len); + if (actual != in_params_len) { + free(in_params); + return PSA_ERROR_CORRUPTION_DETECTED; + } + + switch (func) { + case PSA_CRYPTO_INIT: + ok = psa_crypto_init_wrapper(in_params, in_params_len, + &out_params, &out_params_len); + break; + case PSA_HASH_COMPUTE: + ok = psa_hash_compute_wrapper(in_params, in_params_len, + &out_params, &out_params_len); + break; + } + + free(in_params); + + if (out_params_len > msg.out_size[1]) { + fprintf(stderr, "unable to write %zu bytes into buffer of %zu bytes\n", + out_params_len, msg.out_size[1]); + exit(1); + } + + /* Write the exact amount of data we're returning */ + psa_write(msg.handle, 0, &out_params_len, sizeof(out_params_len)); + + /* And write the data itself */ + if (out_params_len) { + psa_write(msg.handle, 1, out_params, out_params_len); + } + + free(out_params); + + return ok ? PSA_SUCCESS : PSA_ERROR_GENERIC_ERROR; +} diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl new file mode 100644 index 0000000000..62c1a893b7 --- /dev/null +++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl @@ -0,0 +1,1130 @@ +#!/usr/bin/env perl +# +# This is a proof-of-concept script to show that the client and server wrappers +# can be created by a script. It is not hooked into the build, so is run +# manually and the output files are what are to be reviewed. In due course +# this will be replaced by a Python script. +# +use strict; +use Data::Dumper; +use JSON qw(encode_json); + +my $debug = 0; + +# Globals (sorry!) +my %functions = get_functions(); +my @functions = sort keys %functions; + +# get_functions(), called above, returns a data structure for each function +# that we need to create client and server stubs for. In this example Perl script, +# the function declarations we want are in the data section (after __END__ at +# the bottom of this file), but a production Python version should process +# psa_crypto.h. +# +# In this script, the data for psa_crypto_init() looks like: +# +# "psa_crypto_init": { +# "return": { # Info on return type +# "type": "psa_status_t", # Return type +# "name": "status", # Name to be used for this in C code +# "default": "PSA_ERROR_CORRUPTION_DETECTED" # Default value +# }, +# "args": [], # void function, so args empty +# } +# +# The data for psa_hash_compute() looks like: +# +# "psa_hash_compute": { +# "return": { # Information on return type +# "type": "psa_status_t", +# "name": "status", +# "default": "PSA_ERROR_CORRUPTION_DETECTED" +# }, +# "args": [{ +# "type": "psa_algorithm_t", # Type of first argument +# "ctypename": "psa_algorithm_t ", # C type with trailing spaces +# # (so that e.g. `char *` looks ok) +# "name": "alg", +# "is_output": 0 +# }, { +# "type": "const buffer", # Specially created +# "ctypename": "", # (so no C type) +# "name": "input, input_length", # A pair of arguments +# "is_output": 0 # const, so not an output argument +# }, { +# "type": "buffer", # Specially created +# "ctypename": "", +# "name": "hash, hash_size", +# "is_output": 1 # Not const, so output argument +# }, { +# "type": "size_t", # size_t *hash_length +# "ctypename": "size_t ", +# "name": "*hash_length", # * comes into the name +# "is_output": 1 +# } +# ], +# }, +# +# It's possible that a production version might not need both type and ctypename; +# that was done for convenience and future-proofing during development. + +# We'll do psa_crypto_init() first +put_crypto_init_first(\@functions); + +write_function_codes("psa_functions_codes.h"); + +write_client_calls("psa_sim_crypto_client.c"); + +write_server_implementations("psa_sim_crypto_server.c"); + +sub write_function_codes +{ + my ($file) = @_; + + open(my $fh, ">", $file) || die("$0: $file: $!\n"); + + # NOTE: psa_crypto_init() is written manually + + print $fh <", $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 < msg.out_size[1]) { + fprintf(stderr, "unable to write %zu bytes into buffer of %zu bytes\\n", + out_params_len, msg.out_size[1]); + exit(1); + } + + /* Write the exact amount of data we're returning */ + psa_write(msg.handle, 0, &out_params_len, sizeof(out_params_len)); + + /* And write the data itself */ + if (out_params_len) { + psa_write(msg.handle, 1, out_params, out_params_len); + } + + free(out_params); + + return ok ? PSA_SUCCESS : PSA_ERROR_GENERIC_ERROR; +} +EOF + + close($fh); +} + +sub server_implementations_header +{ + return <<'EOF'; +/* THIS FILE WAS AUTO-GENERATED BY psa_sim_generate.pl. DO NOT EDIT!! */ + +/* server implementations */ + +#include +#include + +#include + +#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 +#include + +/* Includes from psasim */ +#include +#include +#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 <> 4); + p[1] = hex_digit(b & 0x0F); + + return 2; +} + +int hex_uint16(char *p, uint16_t b) +{ + hex_byte(p, b >> 8); + hex_byte(p + 2, b & 0xFF); + + return 4; +} + +char human_char(uint8_t c) +{ + return (c >= ' ' && c <= '~') ? (char)c : '.'; +} + +void dump_buffer(const uint8_t *buffer, size_t len) +{ + char line[80]; + + const uint8_t *p = buffer; + + size_t max = (len > 0xFFFF) ? 0xFFFF : len; + + for (size_t i = 0; i < max; i += 16) { + + char *q = line; + + q += hex_uint16(q, (uint16_t)i); + *q++ = ' '; + *q++ = ' '; + + size_t ll = (i + 16 > max) ? (max % 16) : 16; + + size_t j; + for (j = 0; j < ll; j++) { + q += hex_byte(q, p[i + j]); + *q++ = ' '; + } + + while (j++ < 16) { + *q++ = ' '; + *q++ = ' '; + *q++ = ' '; + } + + *q++ = ' '; + + for (j = 0; j < ll; j++) { + *q++ = human_char(p[i + j]); + } + + *q = '\\0'; + + printf("%s\\n", line); + } +} + +void hex_dump(uint8_t *p, size_t n) +{ + for (size_t i = 0; i < n; i++) { + printf("0x%02X ", p[i]); + } + printf("\\n"); +} +EOF +} + +sub output_server_wrapper +{ + my ($fh, $f, $name) = @_; + + my $ret_type = $f->{return}->{type}; + my $ret_name = $f->{return}->{name}; + my $ret_default = $f->{return}->{default}; + + print $fh <{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 <= 0) { # If we have any args (>= 0) + print $fh <= 0) { # If we have any args (>= 0) + print $fh <[$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 <{is_output}, @$args); + + my $sep1 = ($ret_type eq "void") ? ";" : " +"; + + print $fh <{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 <{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 <{return}->{type}; + my $ret_name = $f->{return}->{name}; + my $ret_default = $f->{return}->{default}; + + print $fh <{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 <[$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 <{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 <{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 () { + 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); From a4952f945043bafd1a347f99ac4d68435dcdd250 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:28:08 +0100 Subject: [PATCH 04/11] The PSA sim logs aren't very many lines, so show them during tests Signed-off-by: Tom Cosgrove --- tests/psa-client-server/psasim/test/run_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/psa-client-server/psasim/test/run_test.sh b/tests/psa-client-server/psasim/test/run_test.sh index 06bcc93a82..31429c8bb5 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 @@ 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 From 3ebb880f90478acdf807c19588c0d625838720b9 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:29:39 +0100 Subject: [PATCH 05/11] Hook the new psa_sim_crypto_{client,server} into the build and tests - smoke test client.c becomes a trivial call to psa_crypto_init() - server.c now uses psa_sim_crypto_server.c's psa_crypto_call() - Makefile is updated to build all the modules, and allow a different MAIN - all.sh's test_psasim now tests the simulation of psa_hash_compute() too Signed-off-by: Tom Cosgrove --- tests/psa-client-server/psasim/Makefile | 10 +- .../psasim/src/aut_psa_hash_compute.c | 112 ++++++++++++++++++ tests/psa-client-server/psasim/src/client.c | 41 +------ tests/psa-client-server/psasim/src/server.c | 10 +- tests/scripts/all.sh | 9 ++ 5 files changed, 135 insertions(+), 47 deletions(-) create mode 100644 tests/psa-client-server/psasim/src/aut_psa_hash_compute.c diff --git a/tests/psa-client-server/psasim/Makefile b/tests/psa-client-server/psasim/Makefile index db0c4127f4..45ec45820e 100644 --- a/tests/psa-client-server/psasim/Makefile +++ b/tests/psa-client-server/psasim/Makefile @@ -1,3 +1,5 @@ +MAIN ?= 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 + src/$(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 0000000000..519c0721f7 --- /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 +#include +#include + +#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 550a6e869d..a8c9e08f3e 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 -#include - -/* Includes from psasim */ -#include -#include -#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/server.c b/tests/psa-client-server/psasim/src/server.c index 21b65c709e..77ce2694e3 100644 --- a/tests/psa-client-server/psasim/src/server.c +++ b/tests/psa-client-server/psasim/src/server.c @@ -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) { diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 4f0e9bba14..e0901711d2 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -6228,6 +6228,15 @@ 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="aut_psa_hash_compute.c" + + msg "test psasim running psa_hash_compute" + tests/psa-client-server/psasim/test/run_test.sh + msg "clean psasim" make -C tests/psa-client-server/psasim clean } From 39f8b09f5bf07e98bee3e51f134b47258db31910 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 12:26:53 +0100 Subject: [PATCH 06/11] Add PSA crypto sim serialisation functions needed for the remaining PSA hash APIs Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_sim_serialise.c | 75 +++++++++++++--- .../psasim/src/psa_sim_serialise.h | 82 ++++++++++++++--- .../psasim/src/psa_sim_serialise.pl | 87 ++++++++++++++++--- 3 files changed, 211 insertions(+), 33 deletions(-) diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.c b/tests/psa-client-server/psasim/src/psa_sim_serialise.c index 264509cdf1..78ae9d65d7 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.c +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.c @@ -130,7 +130,9 @@ 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) +int psasim_serialise_unsigned_int(uint8_t **pos, + size_t *remaining, + unsigned int value) { if (*remaining < sizeof(value)) { return 0; @@ -142,7 +144,9 @@ int psasim_serialise_unsigned_int(uint8_t **pos, size_t *remaining, unsigned int return 1; } -int psasim_deserialise_unsigned_int(uint8_t **pos, size_t *remaining, unsigned int *value) +int psasim_deserialise_unsigned_int(uint8_t **pos, + size_t *remaining, + unsigned int *value) { if (*remaining < sizeof(*value)) { return 0; @@ -161,7 +165,9 @@ size_t psasim_serialise_int_needs(int value) return sizeof(value); } -int psasim_serialise_int(uint8_t **pos, size_t *remaining, int value) +int psasim_serialise_int(uint8_t **pos, + size_t *remaining, + int value) { if (*remaining < sizeof(value)) { return 0; @@ -173,7 +179,9 @@ int psasim_serialise_int(uint8_t **pos, size_t *remaining, int value) return 1; } -int psasim_deserialise_int(uint8_t **pos, size_t *remaining, int *value) +int psasim_deserialise_int(uint8_t **pos, + size_t *remaining, + int *value) { if (*remaining < sizeof(*value)) { return 0; @@ -192,7 +200,9 @@ 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) +int psasim_serialise_size_t(uint8_t **pos, + size_t *remaining, + size_t value) { if (*remaining < sizeof(value)) { return 0; @@ -204,7 +214,9 @@ int psasim_serialise_size_t(uint8_t **pos, size_t *remaining, size_t value) return 1; } -int psasim_deserialise_size_t(uint8_t **pos, size_t *remaining, size_t *value) +int psasim_deserialise_size_t(uint8_t **pos, + size_t *remaining, + size_t *value) { if (*remaining < sizeof(*value)) { return 0; @@ -325,12 +337,16 @@ 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) +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) +int psasim_deserialise_psa_status_t(uint8_t **pos, + size_t *remaining, + psa_status_t *value) { return psasim_deserialise_int(pos, remaining, value); } @@ -340,12 +356,51 @@ 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) +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) +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 index 9cca7d8c65..d5eaccf482 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.h +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.h @@ -113,7 +113,9 @@ size_t psasim_serialise_unsigned_int_needs(unsigned int value); * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_serialise_unsigned_int(uint8_t **pos, size_t *remaining, unsigned int value); +int psasim_serialise_unsigned_int(uint8_t **pos, + size_t *remaining, + unsigned int value); /** Deserialise an `unsigned int` from a buffer. * @@ -126,7 +128,9 @@ int psasim_serialise_unsigned_int(uint8_t **pos, size_t *remaining, unsigned int * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_deserialise_unsigned_int(uint8_t **pos, size_t *remaining, unsigned int *value); +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`. @@ -151,7 +155,9 @@ size_t psasim_serialise_int_needs(int value); * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_serialise_int(uint8_t **pos, size_t *remaining, int value); +int psasim_serialise_int(uint8_t **pos, + size_t *remaining, + int value); /** Deserialise an `int` from a buffer. * @@ -164,7 +170,9 @@ int psasim_serialise_int(uint8_t **pos, size_t *remaining, int value); * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_deserialise_int(uint8_t **pos, size_t *remaining, int *value); +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`. @@ -189,7 +197,9 @@ size_t psasim_serialise_size_t_needs(size_t value); * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_serialise_size_t(uint8_t **pos, size_t *remaining, size_t value); +int psasim_serialise_size_t(uint8_t **pos, + size_t *remaining, + size_t value); /** Deserialise a `size_t` from a buffer. * @@ -202,7 +212,9 @@ int psasim_serialise_size_t(uint8_t **pos, size_t *remaining, size_t value); * * \return \c 1 on success ("okay"), \c 0 on error. */ -int psasim_deserialise_size_t(uint8_t **pos, size_t *remaining, size_t *value); +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. @@ -294,7 +306,9 @@ size_t psasim_serialise_psa_status_t_needs(psa_status_t value); * * \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); +int psasim_serialise_psa_status_t(uint8_t **pos, + size_t *remaining, + psa_status_t value); /** Deserialise a `psa_status_t` from a buffer. * @@ -307,7 +321,9 @@ int psasim_serialise_psa_status_t(uint8_t **pos, size_t *remaining, psa_status_t * * \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); +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`. @@ -332,7 +348,9 @@ size_t psasim_serialise_psa_algorithm_t_needs(psa_algorithm_t value); * * \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); +int psasim_serialise_psa_algorithm_t(uint8_t **pos, + size_t *remaining, + psa_algorithm_t value); /** Deserialise a `psa_algorithm_t` from a buffer. * @@ -345,4 +363,48 @@ int psasim_serialise_psa_algorithm_t(uint8_t **pos, size_t *remaining, psa_algor * * \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); +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 index 2a6c3885b7..b89d058516 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl @@ -32,7 +32,10 @@ die($usage) unless $which eq "c" || $which eq "h"; # 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); +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 @@ -110,7 +113,7 @@ sub declare_serialise my $type_d = $type; $type_d =~ s/ /_/g; - return < $#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"; +} From 61ee59f041990b61cb3b277d0c7559662a2368db Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 12:34:27 +0100 Subject: [PATCH 07/11] Add the rest of the psa_hash_xxx() functions to the simulator Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_functions_codes.h | 7 + .../psasim/src/psa_sim_crypto_client.c | 494 +++++++++++++++ .../psasim/src/psa_sim_crypto_server.c | 571 ++++++++++++++++++ .../psasim/src/psa_sim_generate.pl | 266 ++++++++ 4 files changed, 1338 insertions(+) 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 9306be95a1..00937338dd 100644 --- a/tests/psa-client-server/psasim/src/psa_functions_codes.h +++ b/tests/psa-client-server/psasim/src/psa_functions_codes.h @@ -12,7 +12,14 @@ enum { /* 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 index 85c8a3c036..4ac6c4a581 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_client.c @@ -121,6 +121,208 @@ void mbedtls_psa_crypto_free(void) } +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, @@ -205,3 +407,295 @@ fail: 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 index c15b2b0c82..7a8068237d 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c @@ -60,6 +60,227 @@ fail: 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; + + return 1; // success + +fail: + free(result); + return 0; // This shouldn't happen! +} + // Returns 1 for success, 0 for failure int psa_hash_compute_wrapper( uint8_t *in_params, size_t in_params_len, @@ -157,6 +378,328 @@ fail: 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; + + return 1; // success + +fail: + free(result); + 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; + + return 1; // success + +fail: + free(result); + 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; + + return 1; // success + +fail: + free(result); + return 0; // This shouldn't happen! +} + psa_status_t psa_crypto_call(psa_msg_t msg) { int ok = 0; @@ -197,10 +740,38 @@ psa_status_t psa_crypto_call(psa_msg_t msg) 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); diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl index 62c1a893b7..9eef1e52d2 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_generate.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl @@ -1128,3 +1128,269 @@ psa_status_t psa_hash_compute(psa_algorithm_t alg, 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); From e2f0e3012af9386ad68bcdf1fd160cc3e18d8920 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 12:36:09 +0100 Subject: [PATCH 08/11] Extend PSA crypto simulator tests to run psa_hash.c under the simulator Signed-off-by: Tom Cosgrove --- tests/psa-client-server/psasim/Makefile | 4 ++-- tests/scripts/all.sh | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/psa-client-server/psasim/Makefile b/tests/psa-client-server/psasim/Makefile index 45ec45820e..06d3059b4b 100644 --- a/tests/psa-client-server/psasim/Makefile +++ b/tests/psa-client-server/psasim/Makefile @@ -1,4 +1,4 @@ -MAIN ?= client.c +MAIN ?= src/client.c CFLAGS += -Wall -Werror -std=c99 -D_XOPEN_SOURCE=1 -D_POSIX_C_SOURCE=200809L @@ -20,7 +20,7 @@ GENERATED_H_FILES = include/psa_manifest/manifest.h \ include/psa_manifest/sid.h PSA_CLIENT_SRC = src/psa_ff_client.c \ - src/$(MAIN) \ + $(MAIN) \ src/psa_sim_crypto_client.c \ src/psa_sim_serialise.c diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index e0901711d2..c0abf056bd 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -6232,11 +6232,26 @@ component_test_psasim() { # 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="aut_psa_hash_compute.c" + 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 } From ca0c1473545b7c4f5b1de77513c93fd0ae02b892 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 16:52:38 +0100 Subject: [PATCH 09/11] Mark temporary PSA crypto sim Perl scripts as executable Signed-off-by: Tom Cosgrove --- tests/psa-client-server/psasim/src/psa_sim_generate.pl | 0 tests/psa-client-server/psasim/src/psa_sim_serialise.pl | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/psa-client-server/psasim/src/psa_sim_generate.pl mode change 100644 => 100755 tests/psa-client-server/psasim/src/psa_sim_serialise.pl diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl old mode 100644 new mode 100755 diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl old mode 100644 new mode 100755 From 8bfb758e875858479a55768ca2e0f65fb237c7ed Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 22:48:45 +0100 Subject: [PATCH 10/11] PSA crypto sim's server wrappers need to free deseraliased buffers when they're no longer needed Signed-off-by: Tom Cosgrove --- .../psasim/src/psa_sim_crypto_server.c | 33 +++++++++++++++++++ .../psasim/src/psa_sim_generate.pl | 9 ++++- 2 files changed, 41 insertions(+), 1 deletion(-) 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 index 7a8068237d..7e874d16e8 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c @@ -57,6 +57,7 @@ int psa_crypto_init_wrapper( fail: free(result); + return 0; // This shouldn't happen! } @@ -125,6 +126,7 @@ int psa_hash_abort_wrapper( fail: free(result); + return 0; // This shouldn't happen! } @@ -200,6 +202,7 @@ int psa_hash_clone_wrapper( fail: free(result); + return 0; // This shouldn't happen! } @@ -274,10 +277,17 @@ int psa_hash_compare_wrapper( *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! } @@ -371,10 +381,17 @@ int psa_hash_compute_wrapper( *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! } @@ -466,10 +483,15 @@ int psa_hash_finish_wrapper( *out_params = result; *out_params_len = result_size; + free(hash); + return 1; // success fail: free(result); + + free(hash); + return 0; // This shouldn't happen! } @@ -545,6 +567,7 @@ int psa_hash_setup_wrapper( fail: free(result); + return 0; // This shouldn't happen! } @@ -617,10 +640,15 @@ int psa_hash_update_wrapper( *out_params = result; *out_params_len = result_size; + free(input); + return 1; // success fail: free(result); + + free(input); + return 0; // This shouldn't happen! } @@ -693,10 +721,15 @@ int psa_hash_verify_wrapper( *out_params = result; *out_params_len = result_size; + free(hash); + return 1; // success fail: free(result); + + free(hash); + return 0; // This shouldn't happen! } diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl index 9eef1e52d2..7fea72c9d0 100755 --- a/tests/psa-client-server/psasim/src/psa_sim_generate.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl @@ -484,6 +484,8 @@ sub output_server_wrapper my $ret_name = $f->{return}->{name}; my $ret_default = $f->{return}->{default}; + my @buffers = (); # We need to free() these on exit + print $fh < Date: Thu, 30 May 2024 11:12:22 +0100 Subject: [PATCH 11/11] Add missing licences Signed-off-by: Tom Cosgrove --- .../psa-client-server/psasim/src/psa_sim_crypto_server.c | 5 +++++ tests/psa-client-server/psasim/src/psa_sim_generate.pl | 8 ++++++++ tests/psa-client-server/psasim/src/psa_sim_serialise.pl | 3 +++ 3 files changed, 16 insertions(+) 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 index 7e874d16e8..919eb84419 100644 --- a/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c +++ b/tests/psa-client-server/psasim/src/psa_sim_crypto_server.c @@ -2,6 +2,11 @@ /* server implementations */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + #include #include diff --git a/tests/psa-client-server/psasim/src/psa_sim_generate.pl b/tests/psa-client-server/psasim/src/psa_sim_generate.pl index 7fea72c9d0..19c6a0bf4a 100755 --- a/tests/psa-client-server/psasim/src/psa_sim_generate.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_generate.pl @@ -5,6 +5,9 @@ # 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); @@ -251,6 +254,11 @@ sub server_implementations_header /* server implementations */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + #include #include diff --git a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl index b89d058516..5161db1f67 100755 --- a/tests/psa-client-server/psasim/src/psa_sim_serialise.pl +++ b/tests/psa-client-server/psasim/src/psa_sim_serialise.pl @@ -10,6 +10,9 @@ # 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";