From 54b4ccdbf88e04158f63330e0049c388f1b9a6ff Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Wed, 29 May 2024 10:27:01 +0100 Subject: [PATCH] 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 34897b91b..9306be95a 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 000000000..85c8a3c03 --- /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 000000000..c15b2b0c8 --- /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 000000000..62c1a893b --- /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);