diff options
Diffstat (limited to 'library/psa_crypto_pake.c')
-rw-r--r-- | library/psa_crypto_pake.c | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/library/psa_crypto_pake.c b/library/psa_crypto_pake.c new file mode 100644 index 000000000..da66dae7d --- /dev/null +++ b/library/psa_crypto_pake.c @@ -0,0 +1,938 @@ +/* + * PSA PAKE layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include <psa/crypto.h> +#include "psa_crypto_core.h" +#include "psa_crypto_slot_management.h" + +#include <mbedtls/ecjpake.h> +#include <mbedtls/psa_util.h> + +#include <mbedtls/platform.h> +#include <mbedtls/error.h> +#include <string.h> + +/* + * State sequence: + * + * psa_pake_setup() + * | + * |-- In any order: + * | | psa_pake_set_password_key() + * | | psa_pake_set_user() + * | | psa_pake_set_peer() + * | | psa_pake_set_role() + * | + * |--- In any order: (First round input before or after first round output) + * | | + * | |------ In Order + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | + * | |------ In Order: + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | + * |--- In any order: (Second round input before or after second round output) + * | | + * | |------ In Order + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | + * | |------ In Order: + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | + * psa_pake_get_implicit_key() + * psa_pake_abort() + */ + +enum psa_pake_step +{ + PSA_PAKE_STEP_INVALID = 0, + PSA_PAKE_STEP_X1_X2 = 1, + PSA_PAKE_STEP_X2S = 2, + PSA_PAKE_STEP_DERIVE = 3, +}; + +enum psa_pake_state +{ + PSA_PAKE_STATE_INVALID = 0, + PSA_PAKE_STATE_SETUP = 1, + PSA_PAKE_STATE_READY = 2, + PSA_PAKE_OUTPUT_X1_X2 = 3, + PSA_PAKE_OUTPUT_X2S = 4, + PSA_PAKE_INPUT_X1_X2 = 5, + PSA_PAKE_INPUT_X4S = 6, +}; + +/* + * The first PAKE step shares the same sequences of the second PAKE step + * but with a second set of KEY_SHARE/ZK_PUBLIC/ZK_PROOF outputs/inputs. + * It's simpler to share the same sequences numbers of the first + * set of KEY_SHARE/ZK_PUBLIC/ZK_PROOF outputs/inputs in both PAKE steps. + * + * State sequence with step, state & sequence enums: + * => Input & Output Step = PSA_PAKE_STEP_INVALID + * => state = PSA_PAKE_STATE_INVALID + * psa_pake_setup() + * => Input & Output Step = PSA_PAKE_STEP_X1_X2 + * => state = PSA_PAKE_STATE_SETUP + * => sequence = PSA_PAKE_SEQ_INVALID + * | + * |--- In any order: (First round input before or after first round output) + * | | First call of psa_pake_output() or psa_pake_input() sets + * | | state = PSA_PAKE_STATE_READY + * | | + * | |------ In Order: => state = PSA_PAKE_OUTPUT_X1_X2 + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_X2S + * | | + * | |------ In Order: => state = PSA_PAKE_INPUT_X1_X2 + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_INPUT_X4S + * | + * |--- In any order: (Second round input before or after second round output) + * | | + * | |------ In Order: => state = PSA_PAKE_OUTPUT_X2S + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_DERIVE + * | | + * | |------ In Order: => state = PSA_PAKE_INPUT_X4S + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_DERIVE + * | + * psa_pake_get_implicit_key() + * => Input & Output Step = PSA_PAKE_STEP_INVALID + */ +enum psa_pake_sequence +{ + PSA_PAKE_SEQ_INVALID = 0, + PSA_PAKE_X1_STEP_KEY_SHARE = 1, /* also X2S & X4S KEY_SHARE */ + PSA_PAKE_X1_STEP_ZK_PUBLIC = 2, /* also X2S & X4S ZK_PUBLIC */ + PSA_PAKE_X1_STEP_ZK_PROOF = 3, /* also X2S & X4S ZK_PROOF */ + PSA_PAKE_X2_STEP_KEY_SHARE = 4, + PSA_PAKE_X2_STEP_ZK_PUBLIC = 5, + PSA_PAKE_X2_STEP_ZK_PROOF = 6, + PSA_PAKE_SEQ_END = 7, +}; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) +static psa_status_t mbedtls_ecjpake_to_psa_error( int ret ) +{ + switch( ret ) + { + case MBEDTLS_ERR_MPI_BAD_INPUT_DATA: + case MBEDTLS_ERR_ECP_BAD_INPUT_DATA: + case MBEDTLS_ERR_ECP_INVALID_KEY: + case MBEDTLS_ERR_ECP_VERIFY_FAILED: + return( PSA_ERROR_DATA_INVALID ); + case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL: + case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL: + return( PSA_ERROR_BUFFER_TOO_SMALL ); + case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE: + return( PSA_ERROR_NOT_SUPPORTED ); + case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED: + return( PSA_ERROR_CORRUPTION_DETECTED ); + default: + return( PSA_ERROR_GENERIC_ERROR ); + } +} +#endif + +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) +psa_status_t psa_pake_setup( psa_pake_operation_t *operation, + const psa_pake_cipher_suite_t *cipher_suite) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* A context must be freshly initialized before it can be set up. */ + if( operation->alg != PSA_ALG_NONE ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + if( cipher_suite == NULL || + PSA_ALG_IS_PAKE(cipher_suite->algorithm ) == 0 || + ( cipher_suite->type != PSA_PAKE_PRIMITIVE_TYPE_ECC && + cipher_suite->type != PSA_PAKE_PRIMITIVE_TYPE_DH ) || + PSA_ALG_IS_HASH( cipher_suite->hash ) == 0 ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto error; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if( cipher_suite->algorithm == PSA_ALG_JPAKE ) + { + if( cipher_suite->type != PSA_PAKE_PRIMITIVE_TYPE_ECC || + cipher_suite->family != PSA_ECC_FAMILY_SECP_R1 || + cipher_suite->bits != 256 || + cipher_suite->hash != PSA_ALG_SHA_256 ) + { + status = PSA_ERROR_NOT_SUPPORTED; + goto error; + } + + operation->alg = cipher_suite->algorithm; + + mbedtls_ecjpake_init( &operation->ctx.ecjpake ); + + operation->state = PSA_PAKE_STATE_SETUP; + operation->sequence = PSA_PAKE_SEQ_INVALID; + operation->input_step = PSA_PAKE_STEP_X1_X2; + operation->output_step = PSA_PAKE_STEP_X1_X2; + + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + operation->buffer_length = 0; + operation->buffer_offset = 0; + + return( PSA_SUCCESS ); + } + else +#endif + status = PSA_ERROR_NOT_SUPPORTED; + +error: + psa_pake_abort( operation ); + return status; +} + +psa_status_t psa_pake_set_password_key( psa_pake_operation_t *operation, + mbedtls_svc_key_id_t password ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = psa_key_attributes_init(); + psa_key_type_t type; + psa_key_usage_t usage; + psa_key_slot_t *slot = NULL; + + if( operation->alg == PSA_ALG_NONE || + operation->state != PSA_PAKE_STATE_SETUP ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + status = psa_get_key_attributes( password, &attributes ); + if( status != PSA_SUCCESS ) + goto error; + + type = psa_get_key_type( &attributes ); + usage = psa_get_key_usage_flags( &attributes ); + + psa_reset_key_attributes( &attributes ); + + if( type != PSA_KEY_TYPE_PASSWORD && + type != PSA_KEY_TYPE_PASSWORD_HASH ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto error; + } + + if( ( usage & PSA_KEY_USAGE_DERIVE ) == 0 ) { + status = PSA_ERROR_NOT_PERMITTED; + goto error; + } + + if( operation->password != NULL ) + return( PSA_ERROR_BAD_STATE ); + + status = psa_get_and_lock_key_slot_with_policy( password, &slot, + PSA_KEY_USAGE_DERIVE, + PSA_ALG_JPAKE ); + if( status != PSA_SUCCESS ) + return( status ); + + operation->password = mbedtls_calloc( 1, slot->key.bytes ); + if( operation->password == NULL ) + { + psa_unlock_key_slot( slot ); + return( PSA_ERROR_INSUFFICIENT_MEMORY ); + } + memcpy( operation->password, slot->key.data, slot->key.bytes ); + operation->password_len = slot->key.bytes; + + status = psa_unlock_key_slot( slot ); + if( status != PSA_SUCCESS ) + return( status ); + + return( PSA_SUCCESS ); + +error: + psa_pake_abort(operation); + return( status ); +} + +psa_status_t psa_pake_set_user( psa_pake_operation_t *operation, + const uint8_t *user_id, + size_t user_id_len ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if( operation->alg == PSA_ALG_NONE || + operation->state != PSA_PAKE_STATE_SETUP ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + if( user_id_len == 0 || user_id == NULL ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto error; + } + + status = PSA_ERROR_NOT_SUPPORTED; + +error: + psa_pake_abort(operation); + return( status ); +} + +psa_status_t psa_pake_set_peer( psa_pake_operation_t *operation, + const uint8_t *peer_id, + size_t peer_id_len ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if( operation->alg == PSA_ALG_NONE || + operation->state != PSA_PAKE_STATE_SETUP ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + if( peer_id_len == 0 || peer_id == NULL ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto error; + } + + status = PSA_ERROR_NOT_SUPPORTED; + +error: + psa_pake_abort(operation); + return( status ); +} + +psa_status_t psa_pake_set_role( psa_pake_operation_t *operation, + psa_pake_role_t role ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if( operation->alg == PSA_ALG_NONE || + operation->state != PSA_PAKE_STATE_SETUP ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + if( role != PSA_PAKE_ROLE_NONE && + role != PSA_PAKE_ROLE_FIRST && + role != PSA_PAKE_ROLE_SECOND && + role != PSA_PAKE_ROLE_CLIENT && + role != PSA_PAKE_ROLE_SERVER ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto error; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if( operation->alg == PSA_ALG_JPAKE ) + { + if( role != PSA_PAKE_ROLE_CLIENT && + role != PSA_PAKE_ROLE_SERVER ) + return( PSA_ERROR_NOT_SUPPORTED ); + + operation->role = role; + + return( PSA_SUCCESS ); + } + else +#endif + status = PSA_ERROR_NOT_SUPPORTED; + +error: + psa_pake_abort(operation); + return( status ); +} + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) +static psa_status_t psa_pake_ecjpake_setup( psa_pake_operation_t *operation ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecjpake_role role; + + if( operation->role == PSA_PAKE_ROLE_CLIENT ) + role = MBEDTLS_ECJPAKE_CLIENT; + else if( operation->role == PSA_PAKE_ROLE_SERVER ) + role = MBEDTLS_ECJPAKE_SERVER; + else + return( PSA_ERROR_BAD_STATE ); + + if( operation->password_len == 0 ) + return( PSA_ERROR_BAD_STATE ); + + ret = mbedtls_ecjpake_setup( &operation->ctx.ecjpake, + role, + MBEDTLS_MD_SHA256, + MBEDTLS_ECP_DP_SECP256R1, + operation->password, + operation->password_len ); + + mbedtls_platform_zeroize( operation->password, operation->password_len ); + mbedtls_free( operation->password ); + operation->password = NULL; + operation->password_len = 0; + + if( ret != 0 ) + return( mbedtls_ecjpake_to_psa_error( ret ) ); + + operation->state = PSA_PAKE_STATE_READY; + + return( PSA_SUCCESS ); +} +#endif + +static psa_status_t psa_pake_output_internal( + psa_pake_operation_t *operation, + psa_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t length; + + if( operation->alg == PSA_ALG_NONE || + operation->state == PSA_PAKE_STATE_INVALID ) + return( PSA_ERROR_BAD_STATE ); + + if( output == NULL || output_size == 0 || output_length == NULL ) + return( PSA_ERROR_INVALID_ARGUMENT ); + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + /* + * The PSA CRYPTO PAKE and MbedTLS JPAKE API have a different + * handling of output sequencing. + * + * The MbedTLS JPAKE API outputs the whole X1+X2 and X2S steps data + * at once, on the other side the PSA CRYPTO PAKE api requires + * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X2S to be + * retrieved in sequence. + * + * In order to achieve API compatibility, the whole X1+X2 or X2S steps + * data is stored in an intermediate buffer at first step output call, + * and data is sliced down by parsing the ECPoint records in order + * to return the right parts on each step. + */ + if( operation->alg == PSA_ALG_JPAKE ) + { + if( step != PSA_PAKE_STEP_KEY_SHARE && + step != PSA_PAKE_STEP_ZK_PUBLIC && + step != PSA_PAKE_STEP_ZK_PROOF ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + if( operation->state == PSA_PAKE_STATE_SETUP ) { + status = psa_pake_ecjpake_setup( operation ); + if( status != PSA_SUCCESS ) + return( status ); + } + + if( operation->state != PSA_PAKE_STATE_READY && + operation->state != PSA_PAKE_OUTPUT_X1_X2 && + operation->state != PSA_PAKE_OUTPUT_X2S ) + { + return( PSA_ERROR_BAD_STATE ); + } + + if( operation->state == PSA_PAKE_STATE_READY ) + { + if( step != PSA_PAKE_STEP_KEY_SHARE ) + return( PSA_ERROR_BAD_STATE ); + + switch( operation->output_step ) + { + case PSA_PAKE_STEP_X1_X2: + operation->state = PSA_PAKE_OUTPUT_X1_X2; + break; + case PSA_PAKE_STEP_X2S: + operation->state = PSA_PAKE_OUTPUT_X2S; + break; + default: + return( PSA_ERROR_BAD_STATE ); + } + + operation->sequence = PSA_PAKE_X1_STEP_KEY_SHARE; + } + + /* Check if step matches current sequence */ + switch( operation->sequence ) + { + case PSA_PAKE_X1_STEP_KEY_SHARE: + case PSA_PAKE_X2_STEP_KEY_SHARE: + if( step != PSA_PAKE_STEP_KEY_SHARE ) + return( PSA_ERROR_BAD_STATE ); + break; + + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + case PSA_PAKE_X2_STEP_ZK_PUBLIC: + if( step != PSA_PAKE_STEP_ZK_PUBLIC ) + return( PSA_ERROR_BAD_STATE ); + break; + + case PSA_PAKE_X1_STEP_ZK_PROOF: + case PSA_PAKE_X2_STEP_ZK_PROOF: + if( step != PSA_PAKE_STEP_ZK_PROOF ) + return( PSA_ERROR_BAD_STATE ); + break; + + default: + return( PSA_ERROR_BAD_STATE ); + } + + /* Initialize & write round on KEY_SHARE sequences */ + if( operation->state == PSA_PAKE_OUTPUT_X1_X2 && + operation->sequence == PSA_PAKE_X1_STEP_KEY_SHARE ) + { + ret = mbedtls_ecjpake_write_round_one( &operation->ctx.ecjpake, + operation->buffer, + MBEDTLS_PSA_PAKE_BUFFER_SIZE, + &operation->buffer_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE ); + if( ret != 0 ) + return( mbedtls_ecjpake_to_psa_error( ret ) ); + + operation->buffer_offset = 0; + } + else if( operation->state == PSA_PAKE_OUTPUT_X2S && + operation->sequence == PSA_PAKE_X1_STEP_KEY_SHARE ) + { + ret = mbedtls_ecjpake_write_round_two( &operation->ctx.ecjpake, + operation->buffer, + MBEDTLS_PSA_PAKE_BUFFER_SIZE, + &operation->buffer_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE ); + if( ret != 0 ) + return( mbedtls_ecjpake_to_psa_error( ret ) ); + + operation->buffer_offset = 0; + } + + /* + * mbedtls_ecjpake_write_round_xxx() outputs thing in the format + * defined by draft-cragie-tls-ecjpake-01 section 7. The summary is + * that the data for each step is prepended with a length byte, and + * then they're concatenated. Additionally, the server's second round + * output is prepended with a 3-bytes ECParameters structure. + * + * In PSA, we output each step separately, and don't prepend the + * output with a length byte, even less a curve identifier, as that + * information is already available. + */ + if( operation->state == PSA_PAKE_OUTPUT_X2S && + operation->sequence == PSA_PAKE_X1_STEP_KEY_SHARE && + operation->role == PSA_PAKE_ROLE_SERVER ) + { + /* Skip ECParameters, with is 3 bytes (RFC 8422) */ + operation->buffer_offset += 3; + } + + /* Read the length byte then move past it to the data */ + length = operation->buffer[operation->buffer_offset]; + operation->buffer_offset += 1; + + if( operation->buffer_offset + length > operation->buffer_length ) + return( PSA_ERROR_DATA_CORRUPT ); + + if( output_size < length ) + return( PSA_ERROR_BUFFER_TOO_SMALL ); + + memcpy( output, + operation->buffer + operation->buffer_offset, + length ); + *output_length = length; + + operation->buffer_offset += length; + + /* Reset buffer after ZK_PROOF sequence */ + if( ( operation->state == PSA_PAKE_OUTPUT_X1_X2 && + operation->sequence == PSA_PAKE_X2_STEP_ZK_PROOF ) || + ( operation->state == PSA_PAKE_OUTPUT_X2S && + operation->sequence == PSA_PAKE_X1_STEP_ZK_PROOF ) ) + { + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + operation->buffer_length = 0; + operation->buffer_offset = 0; + + operation->state = PSA_PAKE_STATE_READY; + operation->output_step++; + operation->sequence = PSA_PAKE_SEQ_INVALID; + } + else + operation->sequence++; + + return( PSA_SUCCESS ); + } + else +#endif + return( PSA_ERROR_NOT_SUPPORTED ); +} + +psa_status_t psa_pake_output( psa_pake_operation_t *operation, + psa_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + psa_status_t status = psa_pake_output_internal( + operation, step, output, output_size, output_length ); + + if( status != PSA_SUCCESS ) + psa_pake_abort( operation ); + + return( status ); +} + +static psa_status_t psa_pake_input_internal( + psa_pake_operation_t *operation, + psa_pake_step_t step, + const uint8_t *input, + size_t input_length ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if( operation->alg == PSA_ALG_NONE || + operation->state == PSA_PAKE_STATE_INVALID ) + return( PSA_ERROR_BAD_STATE ); + + if( input == NULL || input_length == 0 ) + return( PSA_ERROR_INVALID_ARGUMENT ); + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + /* + * The PSA CRYPTO PAKE and MbedTLS JPAKE API have a different + * handling of input sequencing. + * + * The MbedTLS JPAKE API takes the whole X1+X2 or X4S steps data + * at once as input, on the other side the PSA CRYPTO PAKE api requires + * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X4S to be + * given in sequence. + * + * In order to achieve API compatibility, each X1+X2 or X4S step data + * is stored sequentially in an intermediate buffer and given to the + * MbedTLS JPAKE API on the last step. + * + * This causes any input error to be only detected on the last step. + */ + if( operation->alg == PSA_ALG_JPAKE ) + { + if( step != PSA_PAKE_STEP_KEY_SHARE && + step != PSA_PAKE_STEP_ZK_PUBLIC && + step != PSA_PAKE_STEP_ZK_PROOF ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + const psa_pake_primitive_t prim = PSA_PAKE_PRIMITIVE( + PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256 ); + if( input_length > (size_t) PSA_PAKE_INPUT_SIZE( PSA_ALG_JPAKE, prim, step ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + if( operation->state == PSA_PAKE_STATE_SETUP ) + { + status = psa_pake_ecjpake_setup( operation ); + if( status != PSA_SUCCESS ) + return( status ); + } + + if( operation->state != PSA_PAKE_STATE_READY && + operation->state != PSA_PAKE_INPUT_X1_X2 && + operation->state != PSA_PAKE_INPUT_X4S ) + { + return( PSA_ERROR_BAD_STATE ); + } + + if( operation->state == PSA_PAKE_STATE_READY ) + { + if( step != PSA_PAKE_STEP_KEY_SHARE ) + return( PSA_ERROR_BAD_STATE ); + + switch( operation->input_step ) + { + case PSA_PAKE_STEP_X1_X2: + operation->state = PSA_PAKE_INPUT_X1_X2; + break; + case PSA_PAKE_STEP_X2S: + operation->state = PSA_PAKE_INPUT_X4S; + break; + default: + return( PSA_ERROR_BAD_STATE ); + } + + operation->sequence = PSA_PAKE_X1_STEP_KEY_SHARE; + } + + /* Check if step matches current sequence */ + switch( operation->sequence ) + { + case PSA_PAKE_X1_STEP_KEY_SHARE: + case PSA_PAKE_X2_STEP_KEY_SHARE: + if( step != PSA_PAKE_STEP_KEY_SHARE ) + return( PSA_ERROR_BAD_STATE ); + break; + + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + case PSA_PAKE_X2_STEP_ZK_PUBLIC: + if( step != PSA_PAKE_STEP_ZK_PUBLIC ) + return( PSA_ERROR_BAD_STATE ); + break; + + case PSA_PAKE_X1_STEP_ZK_PROOF: + case PSA_PAKE_X2_STEP_ZK_PROOF: + if( step != PSA_PAKE_STEP_ZK_PROOF ) + return( PSA_ERROR_BAD_STATE ); + break; + + default: + return( PSA_ERROR_BAD_STATE ); + } + + /* + * Copy input to local buffer and format it as the Mbed TLS API + * expects, i.e. as defined by draft-cragie-tls-ecjpake-01 section 7. + * The summary is that the data for each step is prepended with a + * length byte, and then they're concatenated. Additionally, the + * server's second round output is prepended with a 3-bytes + * ECParameters structure - which means we have to prepend that when + * we're a client. + */ + if( operation->state == PSA_PAKE_INPUT_X4S && + operation->sequence == PSA_PAKE_X1_STEP_KEY_SHARE && + operation->role == PSA_PAKE_ROLE_CLIENT ) + { + /* We only support secp256r1. */ + /* This is the ECParameters structure defined by RFC 8422. */ + unsigned char ecparameters[3] = { + 3, /* named_curve */ + 0, 23 /* secp256r1 */ + }; + memcpy( operation->buffer + operation->buffer_length, + ecparameters, sizeof( ecparameters ) ); + operation->buffer_length += sizeof( ecparameters ); + } + + /* Write the length byte */ + operation->buffer[operation->buffer_length] = (uint8_t) input_length; + operation->buffer_length += 1; + + /* Finally copy the data */ + memcpy( operation->buffer + operation->buffer_length, + input, input_length ); + operation->buffer_length += input_length; + + /* Load buffer at each last round ZK_PROOF */ + if( operation->state == PSA_PAKE_INPUT_X1_X2 && + operation->sequence == PSA_PAKE_X2_STEP_ZK_PROOF ) + { + ret = mbedtls_ecjpake_read_round_one( &operation->ctx.ecjpake, + operation->buffer, + operation->buffer_length ); + + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + operation->buffer_length = 0; + + if( ret != 0 ) + return( mbedtls_ecjpake_to_psa_error( ret ) ); + } + else if( operation->state == PSA_PAKE_INPUT_X4S && + operation->sequence == PSA_PAKE_X1_STEP_ZK_PROOF ) + { + ret = mbedtls_ecjpake_read_round_two( &operation->ctx.ecjpake, + operation->buffer, + operation->buffer_length ); + + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + operation->buffer_length = 0; + + if( ret != 0 ) + return( mbedtls_ecjpake_to_psa_error( ret ) ); + } + + if( ( operation->state == PSA_PAKE_INPUT_X1_X2 && + operation->sequence == PSA_PAKE_X2_STEP_ZK_PROOF ) || + ( operation->state == PSA_PAKE_INPUT_X4S && + operation->sequence == PSA_PAKE_X1_STEP_ZK_PROOF ) ) + { + operation->state = PSA_PAKE_STATE_READY; + operation->input_step++; + operation->sequence = PSA_PAKE_SEQ_INVALID; + } + else + operation->sequence++; + + return( PSA_SUCCESS ); + } + else +#endif + return( PSA_ERROR_NOT_SUPPORTED ); +} + +psa_status_t psa_pake_input( psa_pake_operation_t *operation, + psa_pake_step_t step, + const uint8_t *input, + size_t input_length ) +{ + psa_status_t status = psa_pake_input_internal( + operation, step, input, input_length ); + + if( status != PSA_SUCCESS ) + psa_pake_abort( operation ); + + return( status ); +} + +psa_status_t psa_pake_get_implicit_key(psa_pake_operation_t *operation, + psa_key_derivation_operation_t *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if( operation->alg == PSA_ALG_NONE || + operation->state != PSA_PAKE_STATE_READY || + operation->input_step != PSA_PAKE_STEP_DERIVE || + operation->output_step != PSA_PAKE_STEP_DERIVE ) + { + status = PSA_ERROR_BAD_STATE; + goto error; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if( operation->alg == PSA_ALG_JPAKE ) + { + ret = mbedtls_ecjpake_write_shared_key( &operation->ctx.ecjpake, + operation->buffer, + MBEDTLS_PSA_PAKE_BUFFER_SIZE, + &operation->buffer_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE ); + if( ret != 0) + { + psa_pake_abort( operation ); + return( mbedtls_ecjpake_to_psa_error( ret ) ); + } + + status = psa_key_derivation_input_bytes( output, + PSA_KEY_DERIVATION_INPUT_SECRET, + operation->buffer, + operation->buffer_length ); + + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + + psa_pake_abort( operation ); + + return( status ); + } + else +#endif + status = PSA_ERROR_NOT_SUPPORTED; + +error: + psa_key_derivation_abort( output ); + psa_pake_abort( operation ); + + return( status ); +} + +psa_status_t psa_pake_abort(psa_pake_operation_t * operation) +{ + if( operation->alg == PSA_ALG_NONE ) + { + return( PSA_SUCCESS ); + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if( operation->alg == PSA_ALG_JPAKE ) + { + operation->input_step = PSA_PAKE_STEP_INVALID; + operation->output_step = PSA_PAKE_STEP_INVALID; + if( operation->password_len > 0 ) + mbedtls_platform_zeroize( operation->password, operation->password_len ); + mbedtls_free( operation->password ); + operation->password = NULL; + operation->password_len = 0; + operation->role = PSA_PAKE_ROLE_NONE; + mbedtls_platform_zeroize( operation->buffer, MBEDTLS_PSA_PAKE_BUFFER_SIZE ); + operation->buffer_length = 0; + operation->buffer_offset = 0; + mbedtls_ecjpake_free( &operation->ctx.ecjpake ); + } +#endif + + operation->alg = PSA_ALG_NONE; + operation->state = PSA_PAKE_STATE_INVALID; + operation->sequence = PSA_PAKE_SEQ_INVALID; + + return( PSA_SUCCESS ); +} + +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ |