diff options
-rw-r--r-- | src/include/taler_json_lib.h | 6 | ||||
-rw-r--r-- | src/json/json.c | 27 | ||||
-rw-r--r-- | src/json/test_json.c | 59 |
3 files changed, 89 insertions, 3 deletions
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 2a101d269..51ebe6d90 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -563,6 +563,12 @@ enum GNUNET_GenericReturnValue TALER_JSON_parse_agemask (const json_t *root, struct TALER_AgeMask *mask); +/** + * Canonicalize a JSON input to a string according to RFC 8785. + */ +char * +TALER_JSON_canonicalize (const json_t *input); + #endif /* TALER_JSON_LIB_H_ */ /* End of taler_json_lib.h */ diff --git a/src/json/json.c b/src/json/json.c index bf3b2a0e7..da4472522 100644 --- a/src/json/json.c +++ b/src/json/json.c @@ -144,6 +144,9 @@ rfc8785encode (char **inp) if ( (1 == mbl) && (val <= 0x1F) ) { + /* Should not happen, as input is produced by + * JSON stringification */ + GNUNET_break (0); lowdump (&buf, val); } @@ -193,6 +196,12 @@ rfc8785encode (char **inp) } } break; + default: + mbl = 2; + GNUNET_buffer_write (&buf, + pos, + mbl); + break; } } else @@ -1009,6 +1018,24 @@ TALER_deposit_extension_hash (const json_t *extensions, } +char * +TALER_JSON_canonicalize (const json_t *input) +{ + char *wire_enc; + + if (NULL == (wire_enc = json_dumps (input, + JSON_ENCODE_ANY + | JSON_COMPACT + | JSON_SORT_KEYS))) + { + GNUNET_break (0); + return NULL; + } + rfc8785encode (&wire_enc); + return wire_enc; +} + + enum GNUNET_GenericReturnValue TALER_JSON_extensions_config_hash (const json_t *config, struct TALER_ExtensionConfigHash *ech) diff --git a/src/json/test_json.c b/src/json/test_json.c index a8c1c6d8e..5fe51d467 100644 --- a/src/json/test_json.c +++ b/src/json/test_json.c @@ -160,7 +160,7 @@ test_contract (void) GNUNET_assert (GNUNET_OK == TALER_JSON_contract_part_forget (c1, "k2")); - json_dumpf (c1, stderr, JSON_INDENT (2)); + // json_dumpf (c1, stderr, JSON_INDENT (2)); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h2)); @@ -182,7 +182,7 @@ test_contract (void) GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h1)); - json_dumpf (c1, stderr, JSON_INDENT (2)); + // json_dumpf (c1, stderr, JSON_INDENT (2)); json_decref (c1); { char *s; @@ -331,6 +331,57 @@ test_contract (void) static int +test_json_canon (void) +{ + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "Hello\nWorld"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"Hello\\nWorld\"}")); + } + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "Testing “unicode” characters"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"Testing “unicode” characters\"}")); + } + { + json_t *c1; + char *canon; + c1 = json_pack ("{s:s}", + "k1", "low range \x05 chars"); + + canon = TALER_JSON_canonicalize (c1); + GNUNET_assert (NULL != canon); + + printf ("canon: '%s'\n", canon); + + GNUNET_assert (0 == strcmp (canon, + "{\"k1\":\"low range \\u0005 chars\"}")); + } + + + return 0; +} + + +static int test_rfc8785 (void) { struct TALER_PrivateContractHash h1; @@ -348,7 +399,7 @@ test_rfc8785 (void) sizeof (h1)); if (0 != strcmp (s, - "J678K3PW9Y3DG63Z3T7ZYR2P7CEXMVZ2SFPQMABACK9TJRYREPP82542PCJ0P7Y7FAQAMWECDX50XH1RBTWHX6SSJHH6FXRV0JCS6R8")) + "531S33T8ZRGW6548G7T67PMDNGS4Z1D8A2GMB87G3PNKYTW6KGF7Q99XVCGXBKVA2HX6PR5ENJ1PQ5ZTYMMXQB6RM7S82VP7ZG2X5G8")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid reference hash: %s\n", @@ -377,6 +428,8 @@ main (int argc, return 1; if (0 != test_contract ()) return 2; + if (0 != test_json_canon ()) + return 2; if (0 != test_rfc8785 ()) return 2; return 0; |