From 541b665b9f98566b6ac274762c92228ccb15c048 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Thu, 3 Aug 2023 20:33:19 +0530 Subject: [PATCH] feat(provisioning): Generate Salt and verifier pair for given username and password This commit adds a new feature to generate a salt and verifier pair for a given username and password during the provisioning process. This is useful in scenarios where the pairing pin is randomly generated and shown via some interface such as a display or console. - Uses the provided username and password to generate a salt and verifier pair - Adds support for dev mode where the pin/password can still be read from flash --- .../protocomm/include/crypto/srp6a/esp_srp.h | 28 ++++- .../protocomm/src/crypto/srp6a/esp_srp.c | 112 ++++++++++++++---- 2 files changed, 118 insertions(+), 22 deletions(-) diff --git a/components/protocomm/include/crypto/srp6a/esp_srp.h b/components/protocomm/include/crypto/srp6a/esp_srp.h index c9e672af1f..23db7db35c 100644 --- a/components/protocomm/include/crypto/srp6a/esp_srp.h +++ b/components/protocomm/include/crypto/srp6a/esp_srp.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -139,8 +139,32 @@ esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int use char **bytes_B, int *len_B, char **bytes_salt); /** - * @brief Set the Salt and Verifier pre-generated for a given password. + * @brief Generate salt-verifier pair, given username, password and salt length * + * @param[in] username username + * @param[in] username_len length of the username + * @param[in] pass password + * @param[in] pass_len length of the password + * @param[out] bytes_salt generated salt on successful generation, or NULL + * @param[in] salt_len salt length + * @param[out] verifier generated verifier on successful generation, or NULL + * @param[out] verifier_len length of the generated verifier + * @return esp_err_t ESP_OK on success, appropriate error otherwise + * + * @note if API has returned ESP_OK, salt and verifier generated need to be freed by caller + * @note Usually, username and password are not saved on the device. Rather salt and verifier are + * generated outside the device and are embedded. + * this covenience API can be used to generate salt and verifier on the fly for development use case. + * OR for devices which intentionally want to generate different password each time and can send it + * to the client securely. e.g., a device has a display and it shows the pin + */ +esp_err_t esp_srp_gen_salt_verifier(const char *username, int username_len, + const char *pass, int pass_len, + char **bytes_salt, int salt_len, + char **verifier, int *verifier_len); + +/** + * @brief Set the Salt and Verifier pre-generated for a given password. * This should be used only if the actual password is not available. * The public key can then be generated using esp_srp_srv_pubkey_from_salt_verifier() * and not esp_srp_srv_pubkey() diff --git a/components/protocomm/src/crypto/srp6a/esp_srp.c b/components/protocomm/src/crypto/srp6a/esp_srp.c index 55a42e5b06..bf3dd25cc5 100644 --- a/components/protocomm/src/crypto/srp6a/esp_srp.c +++ b/components/protocomm/src/crypto/srp6a/esp_srp.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,7 +18,6 @@ static const char *TAG = "srp6a"; typedef struct esp_srp_handle { - int allocated; esp_ng_type_t type; esp_mpi_ctx_t *ctx; @@ -259,7 +258,7 @@ static esp_mpi_t *calculate_u(esp_srp_handle_t *hd, char *A, int len_A) return calculate_padded_hash(hd, A, len_A, hd->bytes_B, hd->len_B); } -esp_err_t __esp_srp_srv_pubkey(esp_srp_handle_t *hd, char **bytes_B, int *len_B) +static esp_err_t __esp_srp_srv_pubkey(esp_srp_handle_t *hd, char **bytes_B, int *len_B) { esp_mpi_t *k = calculate_k(hd); esp_mpi_t *kv = NULL; @@ -315,47 +314,46 @@ error: return ESP_FAIL; } -esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int username_len, const char *pass, int pass_len, int salt_len, - char **bytes_B, int *len_B, char **bytes_salt) +static esp_err_t _esp_srp_gen_salt_verifier(esp_srp_handle_t *hd, const char *username, int username_len, + const char *pass, int pass_len, int salt_len) { /* Get Salt */ int str_salt_len; esp_mpi_t *x = NULL; hd->s = esp_mpi_new(); - if (! hd->s) { + if (!hd->s) { + ESP_LOGE(TAG, "Failed to allocate bignum s"); goto error; } esp_mpi_get_rand(hd->s, 8 * salt_len, -1, 0); - *bytes_salt = esp_mpi_to_bin(hd->s, &str_salt_len); - if (! *bytes_salt) { + hd->bytes_s = esp_mpi_to_bin(hd->s, &str_salt_len); + if (!hd->bytes_s) { + ESP_LOGE(TAG, "Failed to generate salt of len %d", salt_len); goto error; } - hd->bytes_s = *bytes_salt; hd->len_s = salt_len; ESP_LOGD(TAG, "Salt ->"); - ESP_LOG_BUFFER_HEX_LEVEL(TAG, *bytes_salt, str_salt_len, ESP_LOG_DEBUG); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, hd->bytes_s, str_salt_len, ESP_LOG_DEBUG); /* Calculate X which is simply a hash for all these things */ - x = calculate_x(*bytes_salt, str_salt_len, username, username_len, pass, pass_len); - if (! x) { + x = calculate_x(hd->bytes_s, str_salt_len, username, username_len, pass, pass_len); + if (!x) { + ESP_LOGE(TAG, "Failed to calculate x"); goto error; } hexdump_mpi("x", x); /* v = g^x % N */ hd->v = esp_mpi_new(); - if (! hd->v) { + if (!hd->v) { + ESP_LOGE(TAG, "Failed to allocate bignum v"); goto error; } esp_mpi_a_exp_b_mod_c(hd->v, hd->g, x, hd->n, hd->ctx); hexdump_mpi("Verifier", hd->v); - if (__esp_srp_srv_pubkey(hd, bytes_B, len_B) < 0 ) { - goto error; - } - esp_mpi_free(x); return ESP_OK; @@ -364,9 +362,8 @@ error: esp_mpi_free(hd->s); hd->s = NULL; } - if (*bytes_salt) { - free(*bytes_salt); - *bytes_salt = NULL; + if (hd->bytes_s) { + free(hd->bytes_s); hd->bytes_s = NULL; hd->len_s = 0; } @@ -381,11 +378,86 @@ error: return ESP_FAIL; } +esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int username_len, + const char *pass, int pass_len, int salt_len, + char **bytes_B, int *len_B, char **bytes_salt) +{ + if (!hd || !username || !pass) { + return ESP_ERR_INVALID_ARG; + } + if (ESP_OK != _esp_srp_gen_salt_verifier(hd, username, username_len, pass, pass_len, salt_len)) { + goto error; + } + *bytes_salt = hd->bytes_s; + + if (__esp_srp_srv_pubkey(hd, bytes_B, len_B) < 0 ) { + goto error; + } + return ESP_OK; + +error: + if (hd->s) { + esp_mpi_free(hd->s); + hd->s = NULL; + } + if (*bytes_salt) { + free(*bytes_salt); + *bytes_salt = NULL; + hd->bytes_s = NULL; + hd->len_s = 0; + } + if (hd->v) { + esp_mpi_free(hd->v); + hd->v = NULL; + } + return ESP_FAIL; +} + esp_err_t esp_srp_srv_pubkey_from_salt_verifier(esp_srp_handle_t *hd, char **bytes_B, int *len_B) { + if (!hd || !bytes_B || !len_B) { + return ESP_ERR_INVALID_ARG; + } return __esp_srp_srv_pubkey(hd, bytes_B, len_B); } +/* Generate salt-verifier pair for given username and password */ +esp_err_t esp_srp_gen_salt_verifier(const char *username, int username_len, + const char *pass, int pass_len, + char **bytes_salt, int salt_len, + char **verifier, int *verifier_len) +{ + esp_err_t ret = ESP_FAIL; + + /* allocate and init temporary SRP handle */ + esp_srp_handle_t *srp_hd = esp_srp_init(ESP_NG_3072); + if (!srp_hd) { + ESP_LOGE(TAG, "Failed to initialise security context!"); + return ESP_ERR_NO_MEM; + } + + // get salt and verifier + if (ESP_OK != _esp_srp_gen_salt_verifier(srp_hd, username, username_len, pass, pass_len, salt_len)) { + goto cleanup; + } + + // convert to verifier bytes + *verifier = esp_mpi_to_bin(srp_hd->v, verifier_len); + if (!*verifier) { + ESP_LOGE(TAG, "Failed to allocate verifier bytes!"); + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + + *bytes_salt = srp_hd->bytes_s; + srp_hd->bytes_s = NULL; // so that it won't be freed in `esp_srp_free` step + ret = ESP_OK; + +cleanup: + esp_srp_free(srp_hd); + return ret; +} + esp_err_t esp_srp_set_salt_verifier(esp_srp_handle_t *hd, const char *salt, int salt_len, const char *verifier, int verifier_len) {