AST_JSON_COMPACT,
/*! Formatted for human readability */
AST_JSON_PRETTY,
+ /*! Keys sorted alphabetically */
+ AST_JSON_SORTED,
};
/*!
*/
char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format);
+/*!
+ * \brief Encode a JSON value to a string, with its keys sorted.
+ *
+ * Returned string must be freed by calling ast_json_free().
+ *
+ * \param root JSON value.
+ * \return String encoding of \a root.
+ * \retval NULL on error.
+ */
+#define ast_json_dump_string_sorted(root) ast_json_dump_string_format(root, AST_JSON_SORTED)
+
#define ast_json_dump_str(root, dst) ast_json_dump_str_format(root, dst, AST_JSON_COMPACT)
/*!
*/
static size_t dump_flags(enum ast_json_encoding_format format)
{
- return format == AST_JSON_PRETTY ?
- JSON_INDENT(2) | JSON_PRESERVE_ORDER : JSON_COMPACT;
+ size_t jansson_dump_flags;
+
+ if (format & AST_JSON_PRETTY) {
+ jansson_dump_flags = JSON_INDENT(2);
+ } else {
+ jansson_dump_flags = JSON_COMPACT;
+ }
+
+ if (format & AST_JSON_SORTED) {
+ jansson_dump_flags |= JSON_SORT_KEYS;
+ }
+
+ return jansson_dump_flags;
}
char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
ast_cli(a->fd, "ARI Status:\n");
ast_cli(a->fd, "Enabled: %s\n", AST_CLI_YESNO(conf->general->enabled));
ast_cli(a->fd, "Output format: ");
- switch (conf->general->format) {
- case AST_JSON_COMPACT:
- ast_cli(a->fd, "compact");
- break;
- case AST_JSON_PRETTY:
+ if (conf->general->format & AST_JSON_PRETTY) {
ast_cli(a->fd, "pretty");
- break;
+ } else {
+ ast_cli(a->fd, "compact");
}
ast_cli(a->fd, "\n");
ast_cli(a->fd, "Auth realm: %s\n", conf->general->auth_realm);
return -1;
}
- ast_copy_pj_str(dest_tn, &uri->user, uri->user.slen + 1);
+ /* Remove everything except 0-9, *, and # in telephone number according to RFC 8224
+ * (required by RFC 8225 as part of canonicalization) */
+ {
+ int i;
+ const char *s = uri->user.ptr;
+ char *new_tn = dest_tn;
+ /* We're only removing characters, if anything, so the buffer is guaranteed to be large enough */
+ for (i = 0; i < uri->user.slen; i++) {
+ if (isdigit(*s) || *s == '#' || *s == '*') { /* Only characters allowed */
+ *new_tn++ = *s;
+ }
+ s++;
+ }
+ *new_tn = '\0';
+ ast_debug(4, "Canonicalized telephone number %.*s -> %s\n", (int) uri->user.slen, uri->user.ptr, dest_tn);
+ }
/* x5u (public key URL), attestation, and origid will be added by ast_stir_shaken_sign */
json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: [s]}, s: {s: s}}}",
}
payload = ast_json_object_get(json, "payload");
- dumped_string = ast_json_dump_string(payload);
+ /* Fields must appear in lexiographic order: https://www.rfc-editor.org/rfc/rfc8588.html#section-6
+ * https://www.rfc-editor.org/rfc/rfc8225.html#section-9 */
+ dumped_string = ast_json_dump_string_sorted(payload);
encoded_payload = ast_base64url_encode_string(dumped_string);
ast_json_free(dumped_string);
if (!encoded_payload) {
tmp_json = ast_json_object_get(json, "header");
header = ast_json_dump_string(tmp_json);
tmp_json = ast_json_object_get(json, "payload");
- payload = ast_json_dump_string(tmp_json);
+
+ payload = ast_json_dump_string_sorted(tmp_json);
msg_len = strlen(header) + strlen(payload) + 2;
msg = ast_calloc(1, msg_len);
if (!msg) {
tmp_json = ast_json_object_get(json, "header");
header = ast_json_dump_string(tmp_json);
tmp_json = ast_json_object_get(json, "payload");
- payload = ast_json_dump_string(tmp_json);
+ payload = ast_json_dump_string_sorted(tmp_json);
/* Test empty header parameter */
returned_payload = ast_stir_shaken_verify("", payload, (const char *)signed_payload->signature,