From 0d1eced630f4ed05ad95fdbb4354fd428c9cdbf6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Mar 2016 15:23:11 +0100 Subject: first refactoring of JSON logic to address #4150 and #4237 --- src/wire/Makefile.am | 3 +- src/wire/plugin_wire_sepa.c | 151 +++++++++++++++++++++++++++++++--------- src/wire/plugin_wire_template.c | 6 +- src/wire/plugin_wire_test.c | 119 ++++++++++++++++++------------- src/wire/test_sepa_wireformat.c | 40 ++++++----- 5 files changed, 220 insertions(+), 99 deletions(-) (limited to 'src/wire') diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index eb2e893fa..67a559a36 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -36,7 +36,9 @@ libtaler_plugin_wire_sepa_la_LIBADD = \ $(LTLIBINTL) libtaler_plugin_wire_sepa_la_LDFLAGS = \ $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ -lgnunetutil $(XLIB) @@ -75,4 +77,3 @@ test_sepa_wireformat_LDADD = \ -ljansson \ libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la - diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c index 4d902f962..904afa9bf 100644 --- a/src/wire/plugin_wire_sepa.c +++ b/src/wire/plugin_wire_sepa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016 GNUnet e.V. + Copyright (C) 2016 GNUnet e.V. & Inria TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23,6 +23,8 @@ */ #include "platform.h" #include "taler_wire_plugin.h" +#include "taler_signatures.h" +#include /** @@ -58,6 +60,8 @@ sepa_amount_round (void *cls, struct SepaClosure *sc = cls; uint32_t delta; + if (NULL == sc->currency) + return GNUNET_SYSERR; if (0 != strcasecmp (amount->currency, sc->currency)) { @@ -347,42 +351,116 @@ validate_iban (const char *iban) } +/** + * Verify that the signature in the @a json for /wire/sepa is valid. + * + * @param json json reply with the signature + * @param master_pub public key of the exchange to verify against + * @return #GNUNET_SYSERR if @a json is invalid, + * #GNUNET_NO if the method is unknown, + * #GNUNET_OK if the json is valid + */ +static int +verify_wire_sepa_signature_ok (const json_t *json, + const struct TALER_MasterPublicKeyP *master_pub) +{ + struct TALER_MasterSignatureP exchange_sig; + struct TALER_MasterWireSepaDetailsPS mp; + const char *receiver_name; + const char *iban; + const char *bic; + struct GNUNET_HashContext *hc; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig), + GNUNET_JSON_spec_string ("receiver_name", &receiver_name), + GNUNET_JSON_spec_string ("iban", &iban), + GNUNET_JSON_spec_string ("bic", &bic), + GNUNET_JSON_spec_end() + }; + + if (NULL == master_pub) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Skipping signature check as master public key not given\n"); + return GNUNET_OK; + } + if (GNUNET_OK != + GNUNET_JSON_parse (json, spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + mp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS); + mp.purpose.size = htonl (sizeof (struct TALER_MasterWireSepaDetailsPS)); + hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, + receiver_name, + strlen (receiver_name) + 1); + GNUNET_CRYPTO_hash_context_read (hc, + iban, + strlen (iban) + 1); + GNUNET_CRYPTO_hash_context_read (hc, + bic, + strlen (bic) + 1); + GNUNET_CRYPTO_hash_context_finish (hc, + &mp.h_sepa_details); + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SEPA_DETAILS, + &mp.purpose, + &exchange_sig.eddsa_signature, + &master_pub->eddsa_pub)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + GNUNET_JSON_parse_free (spec); + return GNUNET_OK; +} + + /** * Check if the given wire format JSON object is correctly formatted * + * @param cls the @e cls of this struct with the plugin-specific state * @param wire the JSON wire format object + * @param master_pub public key of the exchange to verify against * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ static int -sepa_wire_validate (const json_t *wire) +sepa_wire_validate (void *cls, + const json_t *wire, + const struct TALER_MasterPublicKeyP *master_pub) { json_error_t error; const char *type; const char *iban; const char *name; const char *bic; - uint64_t r; - const char *address; if (0 != json_unpack_ex ((json_t *) wire, - &error, JSON_STRICT, + &error, 0, "{" - "s:s," /* TYPE: sepa */ - "s:s," /* IBAN: iban */ - "s:s," /* name: beneficiary name */ - "s:s," /* BIC: beneficiary bank's BIC */ - "s:i," /* r: random 64-bit integer nounce */ - "s:s" /* address: address of the beneficiary */ + "s:s," /* type: sepa */ + "s:s," /* iban: IBAN */ + "s:s," /* receiver_name: beneficiary name */ + "s:s" /* bic: beneficiary bank's BIC */ "}", "type", &type, - "IBAN", &iban, - "name", &name, - "bic", &bic, - "r", &r, - "address", &address)) + "iban", &iban, + "receiver_name", &name, + "bic", &bic)) { - TALER_json_warn (error); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "JSON parsing failed at %s:%u: %s (%s)\n", + __FILE__, __LINE__, + error.text, error.source); + json_dumpf (wire, stderr, 0); + fprintf (stderr, "\n"); return GNUNET_SYSERR; } if (0 != strcasecmp (type, @@ -400,6 +478,15 @@ sepa_wire_validate (const json_t *wire) iban); return GNUNET_NO; } + /* FIXME: don't parse again, integrate properly... */ + if (GNUNET_OK != + verify_wire_sepa_signature_ok (wire, + master_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signature invalid\n"); + return GNUNET_NO; + } return GNUNET_YES; } @@ -499,19 +586,21 @@ libtaler_plugin_wire_sepa_init (void *cls) struct TALER_WIRE_Plugin *plugin; sc = GNUNET_new (struct SepaClosure); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "CURRENCY", - &sc->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "CURRENCY"); - GNUNET_free (sc); - return NULL; - } - + if (NULL != cfg) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "CURRENCY", + &sc->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "CURRENCY"); + GNUNET_free (sc); + return NULL; + } + } plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin->cls = sc; plugin->amount_round = &sepa_amount_round; @@ -536,7 +625,7 @@ libtaler_plugin_wire_sepa_done (void *cls) struct TALER_WIRE_Plugin *plugin = cls; struct SepaClosure *sc = plugin->cls; - GNUNET_free (sc->currency); + GNUNET_free_non_null (sc->currency); GNUNET_free (sc); GNUNET_free (plugin); return NULL; diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c index 2e9512e57..91b380c38 100644 --- a/src/wire/plugin_wire_template.c +++ b/src/wire/plugin_wire_template.c @@ -76,11 +76,15 @@ template_amount_round (void *cls, /** * Check if the given wire format JSON object is correctly formatted * + * @param cls the @e cls of this struct with the plugin-specific state * @param wire the JSON wire format object + * @param master_pub public key of the exchange to verify against * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ static int -template_wire_validate (const json_t *wire) +template_wire_validate (void *cls, + const json_t *wire, + const struct TALER_MasterPublicKeyP *master_pub) { GNUNET_break (0); return GNUNET_SYSERR; diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 1d19edf57..70cf82462 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016 GNUnet e.V. + Copyright (C) 2016 GNUnet e.V. & Inria TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -215,6 +215,8 @@ test_amount_round (void *cls, struct TestClosure *tc = cls; uint32_t delta; + if (NULL == tc->currency) + return GNUNET_SYSERR; /* not configured with currency */ if (0 != strcasecmp (amount->currency, tc->currency)) { @@ -235,11 +237,15 @@ test_amount_round (void *cls, * Right now, the only thing we require is a field * "account_number" which must contain a positive 53-bit integer. * + * @param cls the @e cls of this struct with the plugin-specific state * @param wire the JSON wire format object + * @param master_pub public key of the exchange to verify against * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ static int -test_wire_validate (const json_t *wire) +test_wire_validate (void *cls, + const json_t *wire, + const struct TALER_MasterPublicKeyP *master_pub) { json_error_t error; json_int_t account_no; @@ -368,7 +374,9 @@ test_prepare_wire_transfer (void *cls, struct TALER_WIRE_PrepareHandle *pth; if (GNUNET_YES != - test_wire_validate (wire)) + test_wire_validate (cls, + wire, + NULL)) { GNUNET_break (0); return NULL; @@ -456,6 +464,8 @@ test_execute_wire_transfer (void *cls, json_int_t account_no; struct BufFormatP bf; + if (NULL == tc->bank) + return NULL; /* not initialized with configuration, cannot do transfers */ if ( (buf_size <= sizeof (struct BufFormatP)) || ('\0' != buf[buf_size -1]) ) { @@ -476,7 +486,9 @@ test_execute_wire_transfer (void *cls, return NULL; } GNUNET_assert (GNUNET_YES == - test_wire_validate (wire)); + test_wire_validate (NULL, + wire, + NULL)); if (0 != json_unpack_ex (wire, &error, @@ -545,53 +557,56 @@ libtaler_plugin_wire_test_init (void *cls) struct TALER_WIRE_Plugin *plugin; char *uri; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-test", - "BANK_URI", - &uri)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "wire-test", - "BANK_URI"); - return NULL; - } tc = GNUNET_new (struct TestClosure); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "wire-test", - "BANK_ACCOUNT_NO_OUTGOING", - &tc->exchange_account_no)) + if (NULL != cfg) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "wire-test", - "BANK_ACCOUNT_NO_OUTGOING"); - GNUNET_free (uri); - GNUNET_free (tc); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "CURRENCY", - &tc->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "CURRENCY"); - GNUNET_free (uri); - GNUNET_free (tc); - return NULL; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "wire-test", + "BANK_URI", + &uri)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "wire-test", + "BANK_URI"); + GNUNET_free (tc); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "wire-test", + "BANK_ACCOUNT_NO_OUTGOING", + &tc->exchange_account_no)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "wire-test", + "BANK_ACCOUNT_NO_OUTGOING"); + GNUNET_free (uri); + GNUNET_free (tc); + return NULL; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "CURRENCY", + &tc->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "CURRENCY"); + GNUNET_free (uri); + GNUNET_free (tc); + return NULL; + } + tc->bank = TALER_BANK_init (uri); + if (NULL == tc->bank) + { + GNUNET_break (0); + GNUNET_free (tc->currency); + GNUNET_free (tc); + return NULL; + } } - tc->bank = TALER_BANK_init (uri); - if (NULL == tc->bank) - { - GNUNET_break (0); - GNUNET_free (tc->currency); - GNUNET_free (tc); - return NULL; - } - plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin->cls = tc; plugin->amount_round = &test_amount_round; @@ -621,8 +636,12 @@ libtaler_plugin_wire_test_done (void *cls) GNUNET_SCHEDULER_cancel (tc->bt); tc->bt = NULL; } - TALER_BANK_fini (tc->bank); - GNUNET_free (tc->currency); + if (NULL != tc->bank) + { + TALER_BANK_fini (tc->bank); + tc->bank = NULL; + } + GNUNET_free_non_null (tc->currency); GNUNET_free (tc); GNUNET_free (plugin); return NULL; diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c index 5081cbe86..f016cf635 100644 --- a/src/wire/test_sepa_wireformat.c +++ b/src/wire/test_sepa_wireformat.c @@ -28,37 +28,37 @@ /* Valid SEPA data */ static const char * const valid_wire_str = "{ \"type\":\"SEPA\", \ -\"IBAN\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ +\"iban\":\"DE67830654080004822650\", \ +\"receiver_name\":\"GNUnet e.V.\", \ \"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ +\"salt\":\"123456789\", \ \"address\": \"foobar\"}"; /* IBAN has wrong country code */ static const char * const invalid_wire_str = "{ \"type\":\"SEPA\", \ -\"IBAN\":\"XX67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ +\"iban\":\"XX67830654080004822650\", \ +\"receiver_name\":\"GNUnet e.V.\", \ \"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ +\"salt\":\"123456789\", \ \"address\": \"foobar\"}"; /* IBAN has wrong checksum */ static const char * const invalid_wire_str2 = "{ \"type\":\"SEPA\", \ -\"IBAN\":\"DE67830654080004822651\", \ -\"name\":\"GNUnet e.V.\", \ +\"iban\":\"DE67830654080004822651\", \ +\"receiver_name\":\"GNUnet e.V.\", \ \"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ +\"salt\":\"123456789\", \ \"address\": \"foobar\"}"; /* Unsupported wireformat type */ static const char * const unsupported_wire_str = "{ \"type\":\"unsupported\", \ -\"IBAN\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ +\"iban\":\"DE67830654080004822650\", \ +\"receiver_name\":\"GNUnet e.V.\", \ \"bic\":\"GENODEF1SLR\", \ -\"r\":123456789, \ +\"salt\":\"123456789\", \ \"address\": \"foobar\"}"; @@ -85,16 +85,24 @@ main(int argc, GNUNET_assert (NULL != plugin); (void) memset(&error, 0, sizeof(error)); GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (GNUNET_YES != plugin->wire_validate (wire)); + GNUNET_assert (GNUNET_YES != plugin->wire_validate (NULL, + wire, + NULL)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); - GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); + GNUNET_assert (GNUNET_NO == plugin->wire_validate (NULL, + wire, + NULL)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); - GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire)); + GNUNET_assert (GNUNET_NO == plugin->wire_validate (NULL, + wire, + NULL)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); - ret = plugin->wire_validate (wire); + ret = plugin->wire_validate (NULL, + wire, + NULL); json_decref (wire); TALER_WIRE_plugin_unload (plugin); GNUNET_CONFIGURATION_destroy (cfg); -- cgit v1.2.3