diff options
Diffstat (limited to 'src/wire/plugin_wire_sepa.c')
-rw-r--r-- | src/wire/plugin_wire_sepa.c | 292 |
1 files changed, 265 insertions, 27 deletions
diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c index 00d19d4b0..6f01167d9 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 <gnunet/gnunet_json_lib.h> /** @@ -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)) { @@ -348,41 +352,138 @@ validate_iban (const char *iban) /** + * Compute purpose for signing. + * + * @param sepa_name name of the account holder + * @param iban bank account number in IBAN format + * @param bic bank identifier + * @param[out] mp purpose to be signed + */ +static void +compute_purpose (const char *sepa_name, + const char *iban, + const char *bic, + struct TALER_MasterWireDetailsPS *wsd) +{ + struct GNUNET_HashContext *hc; + + wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS)); + wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS); + hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, + "sepa", + strlen ("sepa") + 1); + GNUNET_CRYPTO_hash_context_read (hc, + sepa_name, + strlen (sepa_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, + &wsd->h_sepa_details); +} + + +/** + * 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_MasterWireDetailsPS mp; + const char *name; + const char *iban; + const char *bic; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig), + GNUNET_JSON_spec_string ("name", &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_DEBUG, + "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; + } + compute_purpose (name, + iban, + bic, + &mp); + 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," /* 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" /* bic: beneficiary bank's BIC */ "}", "type", &type, - "IBAN", &iban, + "iban", &iban, "name", &name, - "bic", &bic, - "r", &r, - "address", &address)) + "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,11 +501,144 @@ 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; } /** + * Obtain wire transfer details in the plugin-specific format + * from the configuration. + * + * @param cls closure + * @param cfg configuration with details about wire accounts + * @param account_name which section in the configuration should we parse + * @return NULL if @a cfg fails to have valid wire details for @a account_name + */ +static json_t * +sepa_get_wire_details (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *account_name) +{ + char *sepa_wire_file; + json_error_t err; + json_t *ret; + + /* Fetch reply */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + account_name, + "SEPA_RESPONSE_FILE", + &sepa_wire_file)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + account_name, + "SEPA_RESPONSE_FILE"); + return NULL; + } + ret = json_load_file (sepa_wire_file, + JSON_REJECT_DUPLICATES, + &err); + if (NULL == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse JSON in %s: %s (%s:%u)\n", + sepa_wire_file, + err.text, + err.source, + err.line); + GNUNET_free (sepa_wire_file); + return NULL; + } + if (GNUNET_YES != sepa_wire_validate (cls, + ret, + NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to validate SEPA data in %s\n", + sepa_wire_file); + GNUNET_free (sepa_wire_file); + json_decref (ret); + return NULL; + } + GNUNET_free (sepa_wire_file); + return ret; +} + + +/** + * Sign wire transfer details in the plugin-specific format. + * + * @param cls closure + * @param in wire transfer details in JSON format + * @param key private signing key to use + * @param salt salt to add + * @param[out] sig where to write the signature + * @return #GNUNET_OK on success + */ +static int +sepa_sign_wire_details (void *cls, + const json_t *in, + const struct TALER_MasterPrivateKeyP *key, + const struct GNUNET_HashCode *salt, + struct TALER_MasterSignatureP *sig) +{ + struct TALER_MasterWireDetailsPS wsd; + const char *sepa_name; + const char *iban; + const char *bic; + const char *type; + json_error_t err; + + if (0 != + json_unpack_ex ((json_t *) in, + &err, + 0 /* flags */, + "{s:s, s:s, s:s, s:s}", + "type", &type, + "name", &sepa_name, + "iban", &iban, + "bic", &bic)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to unpack JSON: %s (at %u)\n", + err.text, + err.position); + return GNUNET_SYSERR; + } + if (0 != strcmp (type, + "sepa")) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`type' must be `sepa' for SEPA wire details\n"); + return GNUNET_SYSERR; + } + if (1 != validate_iban (iban)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "IBAN invalid in SEPA wire details\n"); + return GNUNET_SYSERR; + } + compute_purpose (sepa_name, + iban, + bic, + &wsd); + GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv, + &wsd.purpose, + &sig->eddsa_signature); + return GNUNET_OK; +} + + +/** * Prepare for exeuction of a wire transfer. * * @param cls the @e cls of this struct with the plugin-specific state @@ -499,22 +733,26 @@ 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, - "mint", - "CURRENCY", - &sc->currency)) + if (NULL != cfg) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", - "CURRENCY"); - GNUNET_free (sc); - return NULL; + 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; + plugin->get_wire_details = &sepa_get_wire_details; + plugin->sign_wire_details = &sepa_sign_wire_details; plugin->wire_validate = &sepa_wire_validate; plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer; plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel; @@ -536,7 +774,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; |