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