From ae2ce4aaeed35fd077cac016795f069b35189756 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 21 Nov 2021 00:53:11 +0100 Subject: trying to fix #7039 insanity for RFC 8785, Dold: please check --- src/json/Makefile.am | 1 + src/json/json.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/json/test_json.c | 36 ++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) (limited to 'src/json') diff --git a/src/json/Makefile.am b/src/json/Makefile.am index d3e4339fe..2f5ec3f17 100644 --- a/src/json/Makefile.am +++ b/src/json/Makefile.am @@ -22,6 +22,7 @@ libtalerjson_la_LIBADD = \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetjson \ -lgnunetutil \ + -lunistring \ -ljansson \ -lm \ $(XLIB) diff --git a/src/json/json.c b/src/json/json.c index 479a0ae96..af2b84e27 100644 --- a/src/json/json.c +++ b/src/json/json.c @@ -23,6 +23,7 @@ #include #include "taler_util.h" #include "taler_json_lib.h" +#include /** @@ -60,6 +61,161 @@ contains_real (const json_t *json) } +/** + * Dump character in the low range into @a buf + * following RFC 8785. + * + * @param[in,out] buf buffer to modify + * @param val value to dump + */ +static void +lowdump (struct GNUNET_Buffer *buf, + unsigned char val) +{ + char scratch[7]; + + switch (val) + { + case 0x8: + GNUNET_buffer_write (buf, + "\b", + 2); + break; + case 0x9: + GNUNET_buffer_write (buf, + "\t", + 2); + break; + case 0xA: + GNUNET_buffer_write (buf, + "\n", + 2); + break; + case 0xC: + GNUNET_buffer_write (buf, + "\f", + 2); + break; + case 0xD: + GNUNET_buffer_write (buf, + "\r", + 2); + break; + default: + GNUNET_snprintf (scratch, + sizeof (scratch), + "\\u%04x", + (unsigned int) val); + GNUNET_buffer_write (buf, + scratch, + 6); + break; + } +} + + +/** + * Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2). + * + * @param[in,out] inp pointer to string to re-encode + * @return number of bytes in resulting @a inp + */ +static size_t +rfc8785encode (char **inp) +{ + struct GNUNET_Buffer buf = { 0 }; + size_t left = strlen (*inp) + 1; + size_t olen; + char *in = *inp; + const char *pos = in; + + GNUNET_buffer_prealloc (&buf, + left + 40); + buf.warn_grow = 0; /* disable, + 40 is just a wild guess */ + while (1) + { + int mbl = u8_mblen ((unsigned char *) pos, + left); + unsigned char val; + + if (0 == mbl) + break; + val = (unsigned char) *pos; + if ( (1 == mbl) && + (val <= 0x1F) ) + { + lowdump (&buf, + val); + } + else if ( (1 == mbl) && ('\\' == *pos) ) + { + switch (*(pos + 1)) + { + case '\\': + mbl = 2; + GNUNET_buffer_write (&buf, + pos, + mbl); + break; + case 'u': + { + unsigned int num; + uint32_t n32; + unsigned char res[8]; + size_t rlen; + + GNUNET_assert ( (1 == + sscanf (pos + 2, + "%4x", + &num)) || + (1 == + sscanf (pos + 2, + "%4X", + &num)) ); + mbl = 6; + n32 = (uint32_t) num; + rlen = sizeof (res); + u32_to_u8 (&n32, + 1, + res, + &rlen); + if ( (1 == rlen) && + (res[0] <= 0x1F) ) + { + lowdump (&buf, + res[0]); + } + else + { + GNUNET_buffer_write (&buf, + (const char *) res, + rlen); + } + } + break; + } + } + else + { + GNUNET_buffer_write (&buf, + pos, + mbl); + } + left -= mbl; + pos += mbl; + } + + /* 0-terminate buffer */ + GNUNET_buffer_write (&buf, + "", + 1); + GNUNET_free (in); + *inp = GNUNET_buffer_reap (&buf, + &olen); + return olen; +} + + /** * Dump the @a json to a string and hash it. * @@ -97,7 +253,7 @@ dump_and_hash (const json_t *json, GNUNET_break (0); return GNUNET_SYSERR; } - len = strlen (wire_enc) + 1; + len = rfc8785encode (&wire_enc); if (NULL == salt) { GNUNET_CRYPTO_hash (wire_enc, diff --git a/src/json/test_json.c b/src/json/test_json.c index ffc5b33c7..a8c1c6d8e 100644 --- a/src/json/test_json.c +++ b/src/json/test_json.c @@ -330,6 +330,40 @@ test_contract (void) } +static int +test_rfc8785 (void) +{ + struct TALER_PrivateContractHash h1; + json_t *c1; + + c1 = json_pack ("{s:s}", + "k1", "\x08\x0B\t\1\\\x0d"); + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_hash (c1, + &h1)); + { + char *s; + + s = GNUNET_STRINGS_data_to_string_alloc (&h1, + sizeof (h1)); + if (0 != + strcmp (s, + "J678K3PW9Y3DG63Z3T7ZYR2P7CEXMVZ2SFPQMABACK9TJRYREPP82542PCJ0P7Y7FAQAMWECDX50XH1RBTWHX6SSJHH6FXRV0JCS6R8")) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid reference hash: %s\n", + s); + GNUNET_free (s); + json_decref (c1); + return 1; + } + GNUNET_free (s); + } + json_decref (c1); + return 0; +} + + int main (int argc, const char *const argv[]) @@ -343,6 +377,8 @@ main (int argc, return 1; if (0 != test_contract ()) return 2; + if (0 != test_rfc8785 ()) + return 2; return 0; } -- cgit v1.2.3