<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14509">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_stir_shaken: Add outbound INVITE support.<br><br>Integrated STIR/SHAKEN support with outgoing INVITEs. When an INVITE is<br>sent, the caller ID will be checked to see if there is a certificate<br>that corresponds to it. If so, that information will be retrieved and an<br>Identity header will be added to the SIP message. The format is:<br><br>header.payload.signature;info=<public_key_url>alg=ES256;ppt=shaken<br><br>Header, payload, and signature are all BASE64 encoded. The public key<br>URL is retrieved from the certificate. Currently the algorithm and ppt<br>are ES256 and shaken, respectively. This message is signed and can be<br>used for verification on the receiving end.<br><br>Two new configuration options have been added to the certificate object:<br>attestation and origid. The attestation is required and must be A, B, or<br>C. origid is the origination identifier.<br><br>A new utility function has been added as well that takes a string,<br>allocates space, BASE64 encodes it, then returns it, eliminating the<br>need to calculate the size yourself.<br><br>Change-Id: I1f84d6a5839cb2ed152ef4255b380cfc2de662b4<br>---<br>M configs/samples/stir_shaken.conf.sample<br>M include/asterisk/res_stir_shaken.h<br>M include/asterisk/utils.h<br>M main/utils.c<br>M res/res_pjsip_stir_shaken.c<br>M res/res_stir_shaken.c<br>M res/res_stir_shaken/certificate.c<br>M res/res_stir_shaken/certificate.h<br>8 files changed, 233 insertions(+), 21 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/09/14509/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/stir_shaken.conf.sample b/configs/samples/stir_shaken.conf.sample</span><br><span>index 57d1634..71acad2 100644</span><br><span>--- a/configs/samples/stir_shaken.conf.sample</span><br><span>+++ b/configs/samples/stir_shaken.conf.sample</span><br><span>@@ -47,3 +47,9 @@</span><br><span> ;</span><br><span> ; URL to the public key</span><br><span> ;public_key_url=http://mycompany.com/alice.pub</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; Must have an attestation of A, B, or C</span><br><span style="color: hsl(120, 100%, 40%);">+;attestation=C</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; The origination identifier for the certificate</span><br><span style="color: hsl(120, 100%, 40%);">+;origid=MyAsterisk</span><br><span>diff --git a/include/asterisk/res_stir_shaken.h b/include/asterisk/res_stir_shaken.h</span><br><span>index 997054d..cad9282 100644</span><br><span>--- a/include/asterisk/res_stir_shaken.h</span><br><span>+++ b/include/asterisk/res_stir_shaken.h</span><br><span>@@ -21,6 +21,10 @@</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%);">+#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> enum ast_stir_shaken_verification_result {</span><br><span>        AST_STIR_SHAKEN_VERIFY_NOT_PRESENT, /*! No STIR/SHAKEN information was available */</span><br><span>  AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED, /*! Signature verification failed */</span><br><span>@@ -33,6 +37,24 @@</span><br><span> struct ast_json;</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the value for 'signature' from an ast_stir_shaken_payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param payload The payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The signature</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned char *ast_stir_shaken_payload_get_signature(const 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 Retrieve the value for 'public_key_url' from an ast_stir_shaken_payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param payload The payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The public key URL</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_stir_shaken_payload_get_public_key_url(const 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>  * \brief Retrieve the value for 'signature_timeout' from 'general' config object</span><br><span>  *</span><br><span>  * \retval The signature timeout</span><br><span>diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h</span><br><span>index da14eb6..f6280eb 100644</span><br><span>--- a/include/asterisk/utils.h</span><br><span>+++ b/include/asterisk/utils.h</span><br><span>@@ -240,6 +240,19 @@</span><br><span> int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Same as ast_base64encode, but does hte math for you and returns</span><br><span style="color: hsl(120, 100%, 40%);">+ * an encoded string</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The returned string will need to be freed later</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param src The source buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval Encoded string on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_base64encode_string(const char *src);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Decode data from base64</span><br><span>  * \param dst the destination buffer</span><br><span>  * \param src the source buffer</span><br><span>diff --git a/main/utils.c b/main/utils.c</span><br><span>index 59880fd..0b6c6493 100644</span><br><span>--- a/main/utils.c</span><br><span>+++ b/main/utils.c</span><br><span>@@ -398,6 +398,24 @@</span><br><span>       return ast_base64encode_full(dst, src, srclen, max, 0);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Encode to BASE64 and return encoded string */</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_base64encode_string(const char *src)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t encoded_len;</span><br><span style="color: hsl(120, 100%, 40%);">+   char *encoded_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_strlen_zero(src)) {</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%);">+   encoded_len = ((strlen(src) * 4 / 3 + 3) & ~3) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       encoded_string = ast_calloc(1, encoded_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_base64encode(encoded_string, (const unsigned char *)src, strlen(src), encoded_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return encoded_string;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void base64_init(void)</span><br><span> {</span><br><span>    int x;</span><br><span>diff --git a/res/res_pjsip_stir_shaken.c b/res/res_pjsip_stir_shaken.c</span><br><span>index 6866598..4edbba5 100644</span><br><span>--- a/res/res_pjsip_stir_shaken.c</span><br><span>+++ b/res/res_pjsip_stir_shaken.c</span><br><span>@@ -194,10 +194,97 @@</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   static const pj_str_t identity_str = { "Identity", 8 };</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_generic_string_hdr *identity_hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_str_t identity_val;</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_fromto_hdr *old_identity;</span><br><span style="color: hsl(120, 100%, 40%);">+       char *signature;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *public_key_url;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *header;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span style="color: hsl(120, 100%, 40%);">+       RAII_VAR(struct ast_stir_shaken_payload *, ss_payload, NULL, ast_stir_shaken_payload_free);</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(char *, encoded_header, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+     RAII_VAR(char *, encoded_payload, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(char *, combined_str, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t combined_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       old_identity = pjsip_msg_find_hdr_by_name(tdata->msg, &identity_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (old_identity) {</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%);">+   /* x5u (public key URL), attestation, and origid will be added by ast_stir_shaken_sign */</span><br><span style="color: hsl(120, 100%, 40%);">+     json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",</span><br><span style="color: hsl(120, 100%, 40%);">+         "payload", "orig", "tn", session->id.number.str);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!json) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN JSON\n");</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%);">+   ss_payload = ast_stir_shaken_sign(json);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!ss_payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN payload\n");</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%);">+   header = ast_json_object_get(json, "header");</span><br><span style="color: hsl(120, 100%, 40%);">+       encoded_header = ast_base64encode_string(ast_json_dump_string(header));</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!encoded_header) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");</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%);">+   payload = ast_json_object_get(json, "payload");</span><br><span style="color: hsl(120, 100%, 40%);">+     encoded_payload = ast_base64encode_string(ast_json_dump_string(payload));</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!encoded_payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");</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%);">+   signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);</span><br><span style="color: hsl(120, 100%, 40%);">+        public_key_url = ast_stir_shaken_payload_get_public_key_url(ss_payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* The format for the identity header:</span><br><span style="color: hsl(120, 100%, 40%);">+         * header.payload.signature;info=<public_key_url>alg=STIR_SHAKEN_ENCRYPTION_ALGORITHM;ppt=STIR_SHAKEN_PPT</span><br><span style="color: hsl(120, 100%, 40%);">+        */</span><br><span style="color: hsl(120, 100%, 40%);">+   combined_size = strlen(encoded_header) + 1 + strlen(encoded_payload) + 1</span><br><span style="color: hsl(120, 100%, 40%);">+              + strlen(signature) + strlen(";info=<>alg=;ppt=") + strlen(public_key_url)</span><br><span style="color: hsl(120, 100%, 40%);">+            + strlen(STIR_SHAKEN_ENCRYPTION_ALGORITHM) + strlen(STIR_SHAKEN_PPT) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     combined_str = ast_calloc(1, combined_size);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!combined_str) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN identity string\n");</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%);">+     snprintf(combined_str, combined_size, "%s.%s.%s;info=<%s>alg=%s;ppt=%s", encoded_header,</span><br><span style="color: hsl(120, 100%, 40%);">+              encoded_payload, signature, public_key_url, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     identity_val = pj_str(combined_str);</span><br><span style="color: hsl(120, 100%, 40%);">+  identity_hdr = pjsip_generic_string_hdr_create(tdata->pool, &identity_str, &identity_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!identity_hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "Failed to create STIR/SHAKEN Identity header\n");</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%);">+   pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)identity_hdr);</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%);">+static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(session->id.number.str)) {</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%);">+   add_identity_header(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct ast_sip_session_supplement stir_shaken_supplement = {</span><br><span>     .method = "INVITE",</span><br><span>        .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, /* Run AFTER channel creation */</span><br><span>        .incoming_request = stir_shaken_incoming_request,</span><br><span style="color: hsl(120, 100%, 40%);">+     .outgoing_request = stir_shaken_outgoing_request,</span><br><span> };</span><br><span> </span><br><span> static int unload_module(void)</span><br><span>diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c</span><br><span>index 5183c7e..632fd1b 100644</span><br><span>--- a/res/res_stir_shaken.c</span><br><span>+++ b/res/res_stir_shaken.c</span><br><span>@@ -99,6 +99,12 @@</span><br><span>                                   Must be a valid http, or https, URL.</span><br><span>                                        </para></description></span><br><span>                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="attestation"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>Attestation level</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="origid" default=""></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>The origination ID</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                           </configOption></span><br><span>                                <configOption name="caller_id_number" default=""></span><br><span>                                  <synopsis>The caller ID number to match on.</synopsis></span><br><span>                           </configOption></span><br><span>@@ -136,10 +142,6 @@</span><br><span>         </function></span><br><span>  ***/</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#define STIR_SHAKEN_ENCRYPTION_ALGORITHM "ES256"</span><br><span style="color: hsl(0, 100%, 40%);">-#define STIR_SHAKEN_PPT "shaken"</span><br><span style="color: hsl(0, 100%, 40%);">-#define STIR_SHAKEN_TYPE "passport"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static struct ast_sorcery *stir_shaken_sorcery;</span><br><span> </span><br><span> /* Used for AstDB entries */</span><br><span>@@ -184,6 +186,16 @@</span><br><span>        ast_free(payload);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+unsigned char *ast_stir_shaken_payload_get_signature(const struct ast_stir_shaken_payload *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return payload ? payload->signature : 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%);">+char *ast_stir_shaken_payload_get_public_key_url(const struct ast_stir_shaken_payload *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return payload ? payload->public_key_url : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> unsigned int ast_stir_shaken_get_signature_timeout(void)</span><br><span> {</span><br><span>    return ast_stir_shaken_signature_timeout(stir_shaken_general_get());</span><br><span>@@ -1020,6 +1032,7 @@</span><br><span> {</span><br><span>    struct ast_stir_shaken_payload *ss_payload;</span><br><span>  unsigned char *signature;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *public_key_url;</span><br><span>  const char *caller_id_num;</span><br><span>   const char *header;</span><br><span>  const char *payload;</span><br><span>@@ -1049,22 +1062,19 @@</span><br><span>               goto cleanup;</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (stir_shaken_add_x5u(json, stir_shaken_certificate_get_public_key_url(cert))) {</span><br><span style="color: hsl(120, 100%, 40%);">+    public_key_url = stir_shaken_certificate_get_public_key_url(cert);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (stir_shaken_add_x5u(json, public_key_url)) {</span><br><span>             ast_log(LOG_ERROR, "Failed to add 'x5u' (public key URL) to payload\n");</span><br><span>           goto cleanup;</span><br><span>        }</span><br><span style="color: hsl(120, 100%, 40%);">+     ss_payload->public_key_url = ast_strdup(public_key_url);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* TODO: This is just a placeholder for adding 'attest', 'iat', and</span><br><span style="color: hsl(0, 100%, 40%);">-      * 'origid' to the payload. Later, additional logic will need to be</span><br><span style="color: hsl(0, 100%, 40%);">-      * added to determine what these values actually are, but the functions</span><br><span style="color: hsl(0, 100%, 40%);">-  * themselves are ready to go.</span><br><span style="color: hsl(0, 100%, 40%);">-   */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (stir_shaken_add_attest(json, "B")) {</span><br><span style="color: hsl(120, 100%, 40%);">+    if (stir_shaken_add_attest(json, stir_shaken_certificate_get_attestation(cert))) {</span><br><span>           ast_log(LOG_ERROR, "Failed to add 'attest' to payload\n");</span><br><span>                 goto cleanup;</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (stir_shaken_add_origid(json, "asterisk")) {</span><br><span style="color: hsl(120, 100%, 40%);">+     if (stir_shaken_add_origid(json, stir_shaken_certificate_get_origid(cert))) {</span><br><span>                ast_log(LOG_ERROR, "Failed to add 'origid' to payload\n");</span><br><span>                 goto cleanup;</span><br><span>        }</span><br><span>diff --git a/res/res_stir_shaken/certificate.c b/res/res_stir_shaken/certificate.c</span><br><span>index 73b5ce1..5388a7e 100644</span><br><span>--- a/res/res_stir_shaken/certificate.c</span><br><span>+++ b/res/res_stir_shaken/certificate.c</span><br><span>@@ -38,6 +38,10 @@</span><br><span>              AST_STRING_FIELD(public_key_url);</span><br><span>            /*! The caller ID number associated with the certificate */</span><br><span>          AST_STRING_FIELD(caller_id_number);</span><br><span style="color: hsl(120, 100%, 40%);">+           /*! The attestation level for this certificate */</span><br><span style="color: hsl(120, 100%, 40%);">+             AST_STRING_FIELD(attestation);</span><br><span style="color: hsl(120, 100%, 40%);">+                /*! The origination ID for this certificate */</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_STRING_FIELD(origid);</span><br><span>    );</span><br><span>   /*! The private key for the certificate */</span><br><span>   EVP_PKEY *private_key;</span><br><span>@@ -93,20 +97,22 @@</span><br><span> </span><br><span> const char *stir_shaken_certificate_get_public_key_url(struct stir_shaken_certificate *cert)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!cert) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return cert ? cert->public_key_url : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    return cert->public_key_url;</span><br><span style="color: hsl(120, 100%, 40%);">+const char *stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return cert ? cert->attestation : 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%);">+const char *stir_shaken_certificate_get_origid(struct stir_shaken_certificate *cert)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return cert ? cert->origid : NULL;</span><br><span> }</span><br><span> </span><br><span> EVP_PKEY *stir_shaken_certificate_get_private_key(struct stir_shaken_certificate *cert)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!cert) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return cert->private_key;</span><br><span style="color: hsl(120, 100%, 40%);">+  return cert ? cert->private_key : NULL;</span><br><span> }</span><br><span> </span><br><span> static int stir_shaken_certificate_apply(const struct ast_sorcery *sorcery, void *obj)</span><br><span>@@ -119,6 +125,11 @@</span><br><span>                 return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (strlen(cert->attestation) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Attestation must be present\n");</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>  private_key = stir_shaken_read_key(cert->path, 1);</span><br><span>        if (!private_key) {</span><br><span>          return -1;</span><br><span>@@ -244,6 +255,28 @@</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int on_load_attestation(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct stir_shaken_certificate *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (strcmp(var->value, "A") && strcmp(var->value, "B") && strcmp(var->value, "C")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "stir/shaken - attestation level must be A, B, or C (object=%s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_sorcery_object_get_id(cfg));</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_string_field_set(cfg, attestation, var->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%);">+static int attestation_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct stir_shaken_certificate *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    *buf = ast_strdup(cfg->attestation);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #ifdef TEST_FRAMEWORK</span><br><span> </span><br><span> /* Name for test certificaate */</span><br><span>@@ -343,6 +376,9 @@</span><br><span>                on_load_path, path_to_str, NULL, 0, 0);</span><br><span>      ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "public_key_url", "",</span><br><span>             on_load_public_key_url, public_key_url_to_str, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "attestation", "",</span><br><span style="color: hsl(120, 100%, 40%);">+         on_load_attestation, attestation_to_str, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "origid", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, origid));</span><br><span>  ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "caller_id_number", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, caller_id_number));</span><br><span> </span><br><span>  ast_cli_register_multiple(stir_shaken_certificate_cli,</span><br><span>diff --git a/res/res_stir_shaken/certificate.h b/res/res_stir_shaken/certificate.h</span><br><span>index ff30318..6eeb36b 100644</span><br><span>--- a/res/res_stir_shaken/certificate.h</span><br><span>+++ b/res/res_stir_shaken/certificate.h</span><br><span>@@ -45,6 +45,26 @@</span><br><span> const char *stir_shaken_certificate_get_public_key_url(struct stir_shaken_certificate *cert);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the attestation level associated with a certificate</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cert The certificate</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The attestation on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert);</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 Get the origination ID associated with a certificate</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cert The certificate</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The origid on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *stir_shaken_certificate_get_origid(struct stir_shaken_certificate *cert);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Get the private key associated with a certificate</span><br><span>  *</span><br><span>  * \param cert The certificate to get the private key from</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14509">change 14509</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/+/14509"/><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: I1f84d6a5839cb2ed152ef4255b380cfc2de662b4 </div>
<div style="display:none"> Gerrit-Change-Number: 14509 </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>