<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14031">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_stir_shaken: Implemented signing of JSON payload.<br><br>This change provides functions that take in a JSON payload, verify that<br>the contents contain all the mandatory fields and required values (if<br>any), and signs the payload with the private key. Three fields are added<br>to the payload: attest, iat, and origid. As of now, these are just<br>placeholder values that will be set to actual values once the logic is<br>implemented for what to do when an actual payload is received, but the<br>functions to add these values have all been implemented and are ready to<br>use. Upon successful signing and the addition of those three values, a<br>ast_stir_shaken_payload is returned, containing other useful information<br>such as the algorithm, signature, and public_key_url.<br><br>Change-Id: I74fa41c0640ab2a64a1a80110155bd7062f13393<br>---<br>M include/asterisk/res_stir_shaken.h<br>M res/res_stir_shaken.c<br>2 files changed, 363 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/31/14031/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/res_stir_shaken.h b/include/asterisk/res_stir_shaken.h</span><br><span>index 0c589a9..1fff9c5 100644</span><br><span>--- a/include/asterisk/res_stir_shaken.h</span><br><span>+++ b/include/asterisk/res_stir_shaken.h</span><br><span>@@ -18,9 +18,24 @@</span><br><span> #ifndef _RES_STIR_SHAKEN_H</span><br><span> #define _RES_STIR_SHAKEN_H</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/json.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include <openssl/evp.h></span><br><span> #include <openssl/pem.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stir_shaken_payload {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The JWT header */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *header;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! The JWT payload */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_json *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Signature for the payload */</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned char *signature;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! The algorithm used */</span><br><span style="color: hsl(120, 100%, 40%);">+     char *algorithm;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! THe URL to the public key for the certificate */</span><br><span style="color: hsl(120, 100%, 40%);">+  char *public_key_url;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span>  * \brief Retrieve the stir/shaken sorcery context</span><br><span>  *</span><br><span>@@ -37,4 +52,16 @@</span><br><span>  */</span><br><span> EVP_PKEY *ast_stir_shaken_get_private_key(const char *caller_id_number);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Free a STIR/SHAKEN payload</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sign a JSON STIR/SHAKEN payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This function will automatically add the "attest", "iat", and "origid" fields.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stir_shaken_payload *ast_stir_shaken_sign(struct ast_json *json);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _RES_STIR_SHAKEN_H */</span><br><span>diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c</span><br><span>index a6656d0..8c12398 100644</span><br><span>--- a/res/res_stir_shaken.c</span><br><span>+++ b/res/res_stir_shaken.c</span><br><span>@@ -24,6 +24,7 @@</span><br><span> </span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/sorcery.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/time.h"</span><br><span> </span><br><span> #include "asterisk/res_stir_shaken.h"</span><br><span> #include "res_stir_shaken/stir_shaken.h"</span><br><span>@@ -31,6 +32,10 @@</span><br><span> #include "res_stir_shaken/store.h"</span><br><span> #include "res_stir_shaken/certificate.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define STIR_SHAKEN_ENCRYPTION_ALGORITHM "ES256"</span><br><span style="color: hsl(120, 100%, 40%);">+#define STIR_SHAKEN_PPT "shaken"</span><br><span style="color: hsl(120, 100%, 40%);">+#define STIR_SHAKEN_TYPE "passport"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct ast_sorcery *stir_shaken_sorcery;</span><br><span> </span><br><span> struct ast_sorcery *ast_stir_shaken_sorcery(void)</span><br><span>@@ -38,11 +43,342 @@</span><br><span>     return stir_shaken_sorcery;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_json_unref(payload->header);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_json_unref(payload->payload);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(payload->algorithm);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_free(payload->public_key_url);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(payload->signature);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_free(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> EVP_PKEY *ast_stir_shaken_get_private_key(const char *caller_id_number)</span><br><span> {</span><br><span>      return stir_shaken_certificate_get_private_key(caller_id_number);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Verifies the necessary contents are in the JSON and returns a</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_stir_shaken_payload with the extracted values.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json The JSON to verify</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return ast_stir_shaken_payload on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_stir_shaken_payload *stir_shaken_verify_json(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_stir_shaken_payload *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    payload = ast_calloc(1, sizeof(*payload));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to allocate STIR_SHAKEN payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Look through the header first */</span><br><span style="color: hsl(120, 100%, 40%);">+   obj = ast_json_object_get(json, "header");</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'header'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   payload->header = ast_json_deep_copy(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!payload->header) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "STIR_SHAKEN payload failed to copy 'header'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the ppt value for "shaken" */</span><br><span style="color: hsl(120, 100%, 40%);">+      val = ast_json_string_get(ast_json_object_get(obj, "ppt"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!val || strlen(val) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'ppt'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (strcmp(val, STIR_SHAKEN_PPT)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'ppt' did not have "</span><br><span style="color: hsl(120, 100%, 40%);">+                      "required value '%s'\n", STIR_SHAKEN_PPT);</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the typ value for "passport" */</span><br><span style="color: hsl(120, 100%, 40%);">+    val = ast_json_string_get(ast_json_object_get(obj, "typ"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!val || strlen(val) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'typ'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (strcmp(val, STIR_SHAKEN_TYPE)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'typ' did not have "</span><br><span style="color: hsl(120, 100%, 40%);">+                      "required value '%s'\n", STIR_SHAKEN_TYPE);</span><br><span style="color: hsl(120, 100%, 40%);">+         goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the alg value for "ES256" */</span><br><span style="color: hsl(120, 100%, 40%);">+       val = ast_json_string_get(ast_json_object_get(obj, "alg"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!val || strlen(val) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have required field 'alg'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (strcmp(val, STIR_SHAKEN_ENCRYPTION_ALGORITHM)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'alg' did not have "</span><br><span style="color: hsl(120, 100%, 40%);">+                      "required value '%s'\n", STIR_SHAKEN_ENCRYPTION_ALGORITHM);</span><br><span style="color: hsl(120, 100%, 40%);">+         goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   payload->algorithm = ast_strdup(val);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!payload->algorithm) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "STIR/SHAKEN payload failed to copy 'algorithm'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the x5u value for a URL */</span><br><span style="color: hsl(120, 100%, 40%);">+   val = ast_json_string_get(ast_json_object_get(obj, "x5u"));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!val || strlen(val) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have required field 'x5u' (public key URL)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   payload->public_key_url = ast_strdup(val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!payload->public_key_url) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "STIR/SHAKEN payload failed to copy 'x5u' (public key URL)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Now let's check the payload section */</span><br><span style="color: hsl(120, 100%, 40%);">+ obj = ast_json_object_get(json, "payload");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "STIR/SHAKEN payload JWT did not have required field 'payload'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the orig tn value for not NULL */</span><br><span style="color: hsl(120, 100%, 40%);">+    val = ast_json_string_get(ast_json_object_get(ast_json_object_get(obj, "orig"), "tn"));</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!val || strlen(val) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have required field 'orig->tn'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Payload seems sane. Copy it and return on success */</span><br><span style="color: hsl(120, 100%, 40%);">+       payload->payload = ast_json_deep_copy(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!payload->payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "STIR/SHAKEN payload failed to copy 'payload'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return payload;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_stir_shaken_payload_free(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+        return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Signs the payload and returns the signature.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json_str The string representation of the JSON</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param private_key The private key used to sign the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval signature on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static unsigned char *stir_shaken_sign(char *json_str, EVP_PKEY *private_key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  EVP_MD_CTX *mdctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     int ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  unsigned char *encoded_signature = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned char *signature = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      size_t signature_length = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        mdctx = EVP_MD_CTX_create();</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!mdctx) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Failed to create Message Digest Context\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ret = EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, private_key);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ret != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to initialize Message Digest Context\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ret = EVP_DigestSignUpdate(mdctx, json_str, strlen(json_str));</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ret != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to update Message Digest Context\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ret = EVP_DigestSignFinal(mdctx, NULL, &signature_length);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ret != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed initial phase of Message Digest Context signing\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   signature = ast_calloc(1, sizeof(unsigned char) * signature_length);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!signature) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Failed to allocate space for signature\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ret = EVP_DigestSignFinal(mdctx, signature, &signature_length);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ret != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed final phase of Message Digest Context signing\n");</span><br><span style="color: hsl(120, 100%, 40%);">+               goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   encoded_signature = ast_calloc(1, sizeof(unsigned char) * (signature_length + 1));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!encoded_signature) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Failed to allocate space for encoded signature\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_base64encode((char *)encoded_signature, signature, signature_length, signature_length + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+   if (mdctx) {</span><br><span style="color: hsl(120, 100%, 40%);">+          EVP_MD_CTX_destroy(mdctx);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(signature);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return encoded_signature;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Adds the 'attest' field to the JWT.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json The JWT</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param attest The value to set attest to</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int stir_shaken_add_attest(struct ast_json *json, const char *attest)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json *value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     value = ast_json_string_create(attest);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return ast_json_object_set(ast_json_object_get(json, "payload"), "attest", value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Adds the 'origid' field to the JWT.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json The JWT</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param origid The value to set origid to</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int stir_shaken_add_origid(struct ast_json *json, const char *origid)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     value = ast_json_string_create(origid);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!origid) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return ast_json_object_set(ast_json_object_get(json, "payload"), "origid", value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Adds the 'iat' field to the JWT.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json The JWT</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int stir_shaken_add_iat(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_json *value;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct timeval tv;</span><br><span style="color: hsl(120, 100%, 40%);">+    int timestamp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      tv = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+     timestamp = tv.tv_sec + tv.tv_usec / 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+    value = ast_json_integer_create(timestamp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_json_object_set(ast_json_object_get(json, "payload"), "iat", value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stir_shaken_payload *ast_stir_shaken_sign(struct ast_json *json)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_stir_shaken_payload *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned char *signature;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *caller_id_num;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *json_str;</span><br><span style="color: hsl(120, 100%, 40%);">+       EVP_PKEY *private_key;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      payload = stir_shaken_verify_json(json);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* From the payload section of the HSON, get the orig section, and then get</span><br><span style="color: hsl(120, 100%, 40%);">+    * the value of tn. This will be the caller ID number */</span><br><span style="color: hsl(120, 100%, 40%);">+      caller_id_num = ast_json_string_get(ast_json_object_get(ast_json_object_get(</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_json_object_get(json, "payload"), "orig"), "tn"));</span><br><span style="color: hsl(120, 100%, 40%);">+  private_key = stir_shaken_certificate_get_private_key(caller_id_num);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!private_key) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Failed to get private key to sign payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_stir_shaken_payload_free(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   json_str = ast_json_dump_string(json);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!json_str) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Failed to convert JSON to string\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   signature = stir_shaken_sign(json_str, private_key);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!signature) {</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* TODO: This is just a placeholder for adding 'attest', 'iat', and</span><br><span style="color: hsl(120, 100%, 40%);">+    * 'origid' to the payload. Later, additional logic will need to be</span><br><span style="color: hsl(120, 100%, 40%);">+    * added to determine what these values actually are, but the functions</span><br><span style="color: hsl(120, 100%, 40%);">+        * themselves are ready to go.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (stir_shaken_add_attest(json, "B")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Failed to add 'attest' to payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (stir_shaken_add_origid(json, "asterisk")) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Failed to add 'origid' to payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (stir_shaken_add_iat(json)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Failed to add 'iat' to payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   payload->signature = signature;</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_json_free(json_str);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return payload;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_stir_shaken_payload_free(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_json_free(json_str);</span><br><span style="color: hsl(120, 100%, 40%);">+      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int reload_module(void)</span><br><span> {</span><br><span>     if (stir_shaken_sorcery) {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14031">change 14031</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/14031"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I74fa41c0640ab2a64a1a80110155bd7062f13393 </div>
<div style="display:none"> Gerrit-Change-Number: 14031 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>