[Asterisk-code-review] res_stir_shaken: Implemented signature verification. (asterisk[master])

Benjamin Keith Ford asteriskteam at digium.com
Wed Apr 15 13:24:43 CDT 2020


Benjamin Keith Ford has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/14220 )


Change subject: res_stir_shaken: Implemented signature verification.
......................................................................

res_stir_shaken: Implemented signature verification.

There are a lot of moving parts in this patch, but the focus of it is on
the verification of the signature using a public key located at the
public key URL provided in the JSON payload. First, we check the
database to see if we have already downloaded the key. If so, check to
see if it has expired. If it has, redownload from the URL. If we don't
have an entry in the database, just go ahead and download the public
key. The expiration is tested each time we download the file. After
that, read the public key from the file and use it to verify the
signature. All sanity checking is done when the payload is first
received, so the verification is complete once this point is reached.

Change-Id: I3ba4c63880493bf8c7d17a9cfca1af0e934d1a1c
---
M include/asterisk/res_stir_shaken.h
M res/res_stir_shaken.c
M res/res_stir_shaken/certificate.c
M res/res_stir_shaken/stir_shaken.c
M res/res_stir_shaken/stir_shaken.h
5 files changed, 579 insertions(+), 14 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/20/14220/1

diff --git a/include/asterisk/res_stir_shaken.h b/include/asterisk/res_stir_shaken.h
index 16f0139..a65a887 100644
--- a/include/asterisk/res_stir_shaken.h
+++ b/include/asterisk/res_stir_shaken.h
@@ -26,6 +26,21 @@
 struct ast_json;
 
 /*!
+ * \brief Verify a JSON STIR/SHAKEN payload
+ *
+ * \param header The payload header
+ * \param payload The payload section
+ * \param signature The payload signature
+ * \param algorithm The signature algorithm
+ * \param public_key_url The public key URL
+ *
+ * \retval ast_stir_shaken_payload on success
+ * \retval NULL on failure
+ */
+struct ast_stir_shaken_payload *ast_stir_shaken_verify(const char *header, const char *payload, const char *signature,
+	const char *algorithm, const char *public_key_url);
+
+/*!
  * \brief Retrieve the stir/shaken sorcery context
  *
  * \retval The stir/shaken sorcery context
@@ -41,6 +56,11 @@
  * \brief Sign a JSON STIR/SHAKEN payload
  *
  * \note This function will automatically add the "attest", "iat", and "origid" fields.
+ *
+ * \param json The JWT to sign
+ *
+ * \retval ast_stir_shaken_payload on success
+ * \retval NULL on failure
  */
 struct ast_stir_shaken_payload *ast_stir_shaken_sign(struct ast_json *json);
 
diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c
index 3ea7ae9..eaf8d80 100644
--- a/res/res_stir_shaken.c
+++ b/res/res_stir_shaken.c
@@ -18,15 +18,21 @@
 
 /*** MODULEINFO
 	<depend>crypto</depend>
+	<depend>curl</depend>
+	<depend>res_curl</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
+#include <curl/curl.h>
+
 #include "asterisk/module.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/time.h"
 #include "asterisk/json.h"
+#include "asterisk/astdb.h"
+#include "asterisk/paths.h"
 
 #include "asterisk/res_stir_shaken.h"
 #include "res_stir_shaken/stir_shaken.h"
@@ -40,6 +46,21 @@
 
 static struct ast_sorcery *stir_shaken_sorcery;
 
+/* Used for AstDB entries */
+#define AST_DB_FAMILY "STIR_SHAKEN"
+
+/* Used to check CURL headers */
+#define  MAX_HEADER_LENGTH 1023
+
+/* The amount of time (in seconds) to wait before timing out a CURL request */
+#define CURL_TIMEOUT_SEC 7
+
+/* The directory name to store keys in. Appended to ast_config_DATA_DIR */
+#define STIR_SHAKEN_DIR_NAME "stir_shaken_keys"
+
+/* Used for CURL requests */
+#define GLOBAL_USERAGENT "asterisk-libcurl-agent/1.0"
+
 struct ast_stir_shaken_payload {
 	/*! The JWT header */
 	struct ast_json *header;
@@ -74,6 +95,523 @@
 }
 
 /*!
+ * \brief Called when a CURL request completes
+ *
+ * \param data The hash used to store data in AstDB for the public key
+ */
+static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, void *data)
+{
+	size_t realsize;
+	char *hash = data;
+	char *header;
+	char *value;
+
+	realsize = size * nitems;
+
+	if (realsize > MAX_HEADER_LENGTH) {
+		ast_log(LOG_WARNING, "CURL header length is too large (size: '%zu' | max: '%d')\n",
+			realsize, MAX_HEADER_LENGTH);
+		return 0;
+	}
+
+	header = ast_alloca(realsize + 1);
+	memcpy(header, buffer, realsize);
+	header[realsize] = '\0';
+	value = strchr(header, ':');
+	if (!value) {
+		return realsize;
+	}
+	*value++ = '\0';
+
+	if (strcasecmp(header, "Cache-Control") && strcasecmp(header, "Expires")) {
+		return realsize;
+	}
+
+	value = ast_trim_blanks(ast_skip_blanks(value));
+	header = ast_str_to_lower(header);
+
+	ast_db_put(hash, header, value);
+
+	return realsize;
+}
+
+/*!
+ * \brief Sets the expiration for the public key based on the provided fields.
+ * If Cache-Control is present, use it. Otherwise, use Expires.
+ *
+ * \param hash The hash for the public key URL
+ */
+static void set_public_key_expiration(char *hash)
+{
+	char time_buf[32];
+	char value[128] = "";
+	struct timeval actual_expires = ast_tvnow();
+
+	ast_db_get(hash, "cache-control", value, sizeof(value));
+	if (ast_strlen_zero(value)) {
+		char *str_max_age;
+
+		str_max_age = strstr(value, "s-maxage");
+		if (!str_max_age) {
+			str_max_age = strstr(value, "max-age");
+		}
+
+		if (str_max_age) {
+			unsigned int max_age;
+			char *equal = strchr(str_max_age, '=');
+			if (equal && (sscanf(equal + 1, "%30u", &max_age) == 1)) {
+				actual_expires.tv_sec += max_age;
+			}
+		}
+	} else {
+		ast_db_get(hash, "expires", value, sizeof(value));
+		if (ast_strlen_zero(value)) {
+			struct tm expires_time;
+
+			strptime(value, "%a, %d %b %Y %T %z", &expires_time);
+			expires_time.tm_isdst = -1;
+			actual_expires.tv_sec = mktime(&expires_time);
+		}
+	}
+
+	snprintf(time_buf, sizeof(time_buf), "%30lu", actual_expires.tv_sec);
+
+	ast_db_put(hash, "__actual_expires", time_buf);
+}
+
+/*!
+ * \brief Check to see if the public key is expired
+ *
+ * \param public_key_url The public key URL
+ *
+ * \retval 1 if expired
+ * \retval 0 if not expired
+ */
+static int public_key_is_expired(const char *public_key_url)
+{
+	struct timeval current_time = ast_tvnow();
+	struct timeval expires = { .tv_sec = 0, .tv_usec = 0 };
+	char actual_expires[32];
+	char hash[41];
+
+	ast_sha1_hash(hash, public_key_url);
+	ast_db_get(hash, "__actual_expires", actual_expires, sizeof(actual_expires));
+
+	if (ast_strlen_zero(actual_expires)) {
+		return 1;
+	}
+
+	if (sscanf(actual_expires, "%lu", &expires.tv_sec) != 1) {
+		return 1;
+	}
+
+	return ast_tvcmp(current_time, expires) == -1 ? 0 : 1;
+}
+
+/*!
+ * \brief Prepare a CURL instance to use
+ *
+ * \param hash The public key URL hash
+ *
+ * \retval NULL on failure
+ * \retval CURL instance on success
+ */
+static CURL *get_curl_instance(char *hash)
+{
+	CURL *curl;
+
+	curl = curl_easy_init();
+	if (!curl) {
+		return NULL;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT_SEC);
+	curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);
+	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
+	curl_easy_setopt(curl, CURLOPT_HEADERDATA, hash);
+
+	return curl;
+}
+
+/*!
+ * \brief CURL the public key from the provided URL to the specified path
+ *
+ * \param public_key_URL The public key URL
+ * \param path The path to download the file to
+ *
+ * \retval 1 on failure
+ * \retval 0 on success
+ */
+static int curl_public_key(const char *public_key_url, const char *path)
+{
+	FILE *public_key_file;
+	long http_code;
+	CURL *curl;
+	char curl_errbuf[CURL_ERROR_SIZE + 1];
+	char hash[41];
+
+	ast_sha1_hash(hash, public_key_url);
+
+	curl_errbuf[CURL_ERROR_SIZE] = '\0';
+
+	public_key_file = fopen(path, "wb");
+	if (!public_key_file) {
+		ast_log(LOG_ERROR, "Failed to open file '%s' to write public key from '%s': %s (%d)\n",
+			path, public_key_url, strerror(errno), errno);
+		return -1;
+	}
+
+	curl = get_curl_instance(hash);
+	if (!curl) {
+		ast_log(LOG_ERROR, "Failed to set up CURL instance for '%s'\n", public_key_url);
+		fclose(public_key_file);
+		return -1;
+	}
+
+	curl_easy_setopt(curl, CURLOPT_URL, public_key_url);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, public_key_file);
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+
+	if (curl_easy_perform(curl)) {
+		ast_log(LOG_ERROR, "%s\n", curl_errbuf);
+		fclose(public_key_file);
+		return -1;
+	}
+
+	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+	curl_easy_cleanup(curl);
+	fclose(public_key_file);
+
+	if (http_code / 100 != 2) {
+		ast_log(LOG_ERROR, "Failed to retrieve URL '%s': code %ld\n", public_key_url, http_code);
+		return -1;
+	}
+
+	set_public_key_expiration(hash);
+
+	return 0;
+}
+
+/*!
+ * \brief Get the file name from the provided URL
+ *
+ * \param url The URL
+ */
+static char *get_filename_from_url(const char *url)
+{
+	size_t size;
+	char *res, *buf;
+
+	buf = ast_strdup(url);
+	size = strlen(buf);
+
+	while (size && buf[size - 1] == '/') {
+		size--;
+		buf[size] = '\0';
+	}
+
+	res = strrchr(buf, '/');
+	if (res && res[1] != '\0') {
+		res = (res + 1);
+	} else {
+		res = buf;
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Returns the path to the downloaded file for the provided URL
+ *
+ * \param public_key_url The public key URL
+ *
+ * \retval Empty string if not present in AstDB
+ * \retval The file path if present in AstDB
+ */
+static char *get_path_to_public_key(const char *public_key_url)
+{
+	char hash[41];
+	char file_path[256];
+
+	ast_sha1_hash(hash, public_key_url);
+
+	ast_db_get(hash, "path", file_path, sizeof(file_path));
+
+	if (ast_strlen_zero(file_path)) {
+		file_path[0] = '\0';
+	}
+
+	return ast_strdup(file_path);
+}
+
+/*!
+ * \brief Add the public key and its file path to AstDB
+ *
+ * \param public_key_url The public key URL
+ * \param filepath The path to the file
+ */
+static void add_public_key_to_astdb(const char *public_key_url, const char *filepath)
+{
+	char hash[41];
+
+	ast_sha1_hash(hash, public_key_url);
+
+	ast_db_put(AST_DB_FAMILY, public_key_url, hash);
+	ast_db_put(hash, "path", filepath);
+}
+
+/*!
+ * \brief Remove the public key and associated information from AstDB
+ *
+ * \param public_key_url The public key URL
+ */
+static void remove_public_key_from_astdb(const char *public_key_url)
+{
+	char hash[41];
+	char filepath[256];
+
+	ast_sha1_hash(hash, public_key_url);
+
+	/* Remove this public key from storage */
+	ast_db_get(hash, "path", filepath, sizeof(filepath));
+	remove(filepath);
+
+	ast_db_del(AST_DB_FAMILY, public_key_url);
+	ast_db_deltree(hash, NULL);
+}
+
+/*!
+ * \brief Verifies the signature using a public key
+ *
+ * \param msg The payload
+ * \param signature The signature to verify
+ * \param public_key The public key used for verification
+ *
+ * \retval -1 on failure
+ * \retval 0 on success
+ */
+static int stir_shaken_verify_signature(const char *msg, const char *signature, EVP_PKEY *public_key)
+{
+	EVP_MD_CTX *mdctx = NULL;
+	int ret = 0;
+	unsigned char *decoded_signature;
+	size_t signature_length, decoded_signature_length, padding = 0;
+
+	mdctx = EVP_MD_CTX_create();
+	if (!mdctx) {
+		ast_log(LOG_ERROR, "Failed to create Message Digest Context\n");
+		return -1;
+	}
+
+	ret = EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, public_key);
+	if (ret != 1) {
+		ast_log(LOG_ERROR, "Failed to initialize Message Digest Context\n");
+		EVP_MD_CTX_destroy(mdctx);
+		return -1;
+	}
+
+	ret = EVP_DigestVerifyUpdate(mdctx, (unsigned char *)msg, strlen(msg));
+	if (ret != 1) {
+		ast_log(LOG_ERROR, "Failed to update Message Digest Context\n");
+		EVP_MD_CTX_destroy(mdctx);
+		return -1;
+	}
+
+	/* We need to decode the signature from base64 to bytes */
+	signature_length = strlen(signature);
+
+	if (signature[signature_length - 1] == '=' && signature[signature_length - 2] == '=') {
+		padding = 2;
+	} else if (signature[signature_length - 1] == '=') {
+		padding = 1;
+	}
+
+	decoded_signature_length = signature_length * 3 / 4 - padding;
+	decoded_signature = ast_calloc(1, decoded_signature_length);
+	ast_base64decode(decoded_signature, signature, decoded_signature_length);
+
+	ret = EVP_DigestVerifyFinal(mdctx, decoded_signature, strlen((const char *)decoded_signature));
+	if (ret != 1) {
+		ast_log(LOG_ERROR, "Failed final phase of signature verification\n");
+		EVP_MD_CTX_destroy(mdctx);
+		ast_free(decoded_signature);
+		return -1;
+	}
+
+	EVP_MD_CTX_destroy(mdctx);
+	ast_free(decoded_signature);
+
+	return 0;
+}
+
+struct ast_stir_shaken_payload *ast_stir_shaken_verify(const char *header, const char *payload, const char *signature,
+	const char *algorithm, const char *public_key_url)
+{
+	struct ast_stir_shaken_payload *ret_payload;
+	EVP_PKEY *public_key;
+	char file_path[256], default_path[256], stir_shaken_dir[256];
+	char *filename;
+	struct ast_json *json_header, *json_payload;
+	int curl = 0;
+
+	if (ast_strlen_zero(header)) {
+		ast_log(LOG_ERROR, "'header' is required for STIR/SHAKEN verification\n");
+		return NULL;
+	}
+
+	if (ast_strlen_zero(payload)) {
+		ast_log(LOG_ERROR, "'payload' is required for STIR/SHAKEN verification\n");
+		return NULL;
+	}
+
+	if (ast_strlen_zero(signature)) {
+		ast_log(LOG_ERROR, "'signature' is required for STIR/SHAKEN verification\n");
+		return NULL;
+	}
+
+	if (ast_strlen_zero(algorithm)) {
+		ast_log(LOG_ERROR, "'algorithm' is required for STIR/SHAKEN verification\n");
+		return NULL;
+	}
+
+	if (ast_strlen_zero(public_key_url)) {
+		ast_log(LOG_ERROR, "'public_key_url' is required for STIR/SHAKEN verification\n");
+		return NULL;
+	}
+
+	/* Set up the default path and create directory if needed */
+	snprintf(stir_shaken_dir, sizeof(stir_shaken_dir), "%s/%s", ast_config_AST_DATA_DIR, STIR_SHAKEN_DIR_NAME);
+	ast_mkdir(stir_shaken_dir, 0777);
+	filename = get_filename_from_url(public_key_url);
+	snprintf(default_path, sizeof(default_path), "%s/%s", stir_shaken_dir, filename);
+
+	/* Check to see if we have already downloaded this public key */
+	snprintf(file_path, sizeof(file_path), "%s", get_path_to_public_key(public_key_url));
+
+	/* If we don't have an entry in AstDB, CURL from the provided URL */
+	if (ast_strlen_zero(file_path)) {
+
+		/* Remove this entry from the database, since we will be
+		 * downloading a new file anyways.
+		 */
+		remove_public_key_from_astdb(public_key_url);
+
+		if (curl_public_key(public_key_url, default_path)) {
+			ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_key_url);
+			return NULL;
+		}
+
+		/* Signal that we have already downloaded a new file, no reason to do it again */
+		curl = 1;
+
+		snprintf(file_path, sizeof(file_path), "%s", default_path);
+
+		/* We should have a successful download at this point, so
+		 * add an entry to the database.
+		 */
+		add_public_key_to_astdb(public_key_url, file_path);
+	}
+
+	/* Check to see if the key we downloaded (or already had) is expired */
+	if (public_key_is_expired(public_key_url)) {
+
+		ast_debug(3, "Public key '%s' is expired\n", public_key_url);
+
+		remove_public_key_from_astdb(public_key_url);
+
+		/* We downloaded a new file, so if it's expired, there's nothing we can do */
+		if (curl) {
+			ast_log(LOG_ERROR, "Newly downloaded public key '%s' is expired\n", file_path);
+			return NULL;
+		}
+
+		if (curl_public_key(public_key_url, default_path)) {
+			ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_key_url);
+			return NULL;
+		}
+
+		if (public_key_is_expired(public_key_url)) {
+			ast_log(LOG_ERROR, "Newly downloaded public key '%s' is expired\n", file_path);
+			return NULL;
+		}
+
+		curl = 1;
+
+		add_public_key_to_astdb(public_key_url, file_path);
+	}
+
+	/* First attempt to read the key. If it fails, try downloading the file,
+	 * unless we already did. Check for expiration again */
+	public_key = read_key(file_path, 0);
+	if (!public_key) {
+
+		ast_debug(3, "Failed first read of public key file '%s'\n", file_path);
+
+		remove_public_key_from_astdb(public_key_url);
+
+		/* We downloaded a new file and still couldn't read the public key */
+		if (curl) {
+			ast_log(LOG_ERROR, "Failed to read public key from newly downloaded file '%s'\n", file_path);
+			return NULL;
+		}
+
+		if (curl_public_key(public_key_url, file_path)) {
+			ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_key_url);
+			return NULL;
+		}
+
+		if (public_key_is_expired(public_key_url)) {
+			ast_log(LOG_ERROR, "Newly downloaded public key '%s' is expired\n", file_path);
+			return NULL;
+		}
+
+		public_key = read_key(file_path, 0);
+		if (!public_key) {
+			ast_log(LOG_ERROR, "Failed to read public key from '%s'\n", file_path);
+			return NULL;
+		}
+
+		add_public_key_to_astdb(public_key_url, file_path);
+	}
+
+	if (stir_shaken_verify_signature(payload, signature, public_key)) {
+		ast_log(LOG_ERROR, "Failed to verify signature\n");
+		return NULL;
+	}
+
+	ret_payload = ast_calloc(1, sizeof(*ret_payload));
+	if (!ret_payload) {
+		ast_log(LOG_ERROR, "Failed to allocate STIR/SHAKEN payload\n");
+		return NULL;
+	}
+
+	json_header = ast_json_pack(header);
+	if (!json_header) {
+		ast_log(LOG_ERROR, "Failed to create JSON from header\n");
+		ast_stir_shaken_payload_free(ret_payload);
+		return NULL;
+	}
+	ret_payload->header = json_header;
+
+	json_payload = ast_json_pack(payload);
+	if (!json_payload) {
+		ast_log(LOG_ERROR, "Failed to create JSON from payload\n");
+		ast_stir_shaken_payload_free(ret_payload);
+		return NULL;
+	}
+	ret_payload->payload = json_payload;
+
+	ret_payload->signature = (unsigned char *)ast_strdupa(signature);
+	ret_payload->algorithm = ast_strdupa(algorithm);
+	ret_payload->public_key_url = ast_strdupa(public_key_url);
+
+	return ret_payload;
+}
+
+/*!
  * \brief Verifies the necessary contents are in the JSON and returns a
  * ast_stir_shaken_payload with the extracted values.
  *
@@ -90,7 +628,7 @@
 
 	payload = ast_calloc(1, sizeof(*payload));
 	if (!payload) {
-		ast_log(LOG_ERROR, "Failed to allocate STIR_SHAKEN payload\n");
+		ast_log(LOG_ERROR, "Failed to allocate STIR/SHAKEN payload\n");
 		goto cleanup;
 	}
 
@@ -234,7 +772,7 @@
 	/* There are 6 bits to 1 base64 digit, so in order to get the size of the base64 encoded
 	 * signature, we need to multiply by the number of bits in a byte and divide by 6. Since
 	 * there's rounding when doing base64 conversions, add 3 bytes, just in case, and account
-	 * for padding. Add another byte for the NULL-terminator so we don't lose data.
+	 * for padding. Add another byte for the NULL-terminator.
 	 */
 	encoded_length = ((signature_length * 4 / 3 + 3) & ~3) + 1;
 	encoded_signature = ast_calloc(1, encoded_length);
diff --git a/res/res_stir_shaken/certificate.c b/res/res_stir_shaken/certificate.c
index 812fc1e..cc4a144 100644
--- a/res/res_stir_shaken/certificate.c
+++ b/res/res_stir_shaken/certificate.c
@@ -119,7 +119,7 @@
 		return -1;
 	}
 
-	private_key = read_private_key(cert->path);
+	private_key = read_key(cert->path, 1);
 	if (!private_key) {
 		return -1;
 	}
diff --git a/res/res_stir_shaken/stir_shaken.c b/res/res_stir_shaken/stir_shaken.c
index 5f5c054..bb6cc18 100644
--- a/res/res_stir_shaken/stir_shaken.c
+++ b/res/res_stir_shaken/stir_shaken.c
@@ -83,9 +83,9 @@
 	return NULL;
 }
 
-EVP_PKEY *read_private_key(const char *path)
+EVP_PKEY *read_key(const char *path, int priv)
 {
-	EVP_PKEY *private_key = NULL;
+	EVP_PKEY *key = NULL;
 	FILE *fp;
 
 	fp = fopen(path, "r");
@@ -94,20 +94,26 @@
 		return NULL;
 	}
 
-	if (!PEM_read_PrivateKey(fp, &private_key, NULL, NULL)) {
-		ast_log(LOG_ERROR, "Failed to read private key from file '%s'\n", path);
+	if (priv) {
+		key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
+	} else {
+		key = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
+	}
+
+	if (!key) {
+		ast_log(LOG_ERROR, "Failed to read %s key from file '%s'\n", priv ? "private" : "public", path);
 		fclose(fp);
 		return NULL;
 	}
 
-	if (EVP_PKEY_id(private_key) != EVP_PKEY_EC) {
-		ast_log(LOG_ERROR, "Private key from '%s' must be of type EVP_PKEY_EC\n", path);
+	if (EVP_PKEY_id(key) != EVP_PKEY_EC) {
+		ast_log(LOG_ERROR, "%s key from '%s' must be of type EVP_PKEY_EC\n", priv ? "private" : "public", path);
 		fclose(fp);
-		EVP_PKEY_free(private_key);
+		EVP_PKEY_free(key);
 		return NULL;
 	}
 
 	fclose(fp);
 
-	return private_key;
+	return key;
 }
diff --git a/res/res_stir_shaken/stir_shaken.h b/res/res_stir_shaken/stir_shaken.h
index 933b3bb..4e6d305 100644
--- a/res/res_stir_shaken/stir_shaken.h
+++ b/res/res_stir_shaken/stir_shaken.h
@@ -42,13 +42,14 @@
 char *stir_shaken_tab_complete_name(const char *word, struct ao2_container *container);
 
 /*!
- * \brief Reads the private key from the specified path
+ * \brief Reads the public (or private) key from the specified path
  *
  * \param path The path to the file containing the private key
+ * \param priv Specify 0 for public, 1 for private
  *
  * \retval NULL on failure
- * \retval The private key on success
+ * \retval The public/private key on success
  */
-EVP_PKEY *read_private_key(const char *path);
+EVP_PKEY *read_key(const char *path, int priv);
 
 #endif /* _STIR_SHAKEN_H */

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/14220
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I3ba4c63880493bf8c7d17a9cfca1af0e934d1a1c
Gerrit-Change-Number: 14220
Gerrit-PatchSet: 1
Gerrit-Owner: Benjamin Keith Ford <bford at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20200415/fdfad715/attachment-0001.html>


More information about the asterisk-code-review mailing list