diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index ec75c3ec0..78d424b80 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -127,8 +127,8 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, char *wire_enc; size_t len; struct GNUNET_MINT_ParseFieldSpec spec[] = { - TALER_MINT_PARSE_VARIABLE ("denom_pub"), - TALER_MINT_PARSE_VARIABLE ("ubsig"), + TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &deposit.coin.denom_pub), + TALER_MINT_PARSE_RSA_SIGNATURE ("ubsig", &deposit.coin.denom_sig), TALER_MINT_PARSE_FIXED ("coin_pub", &deposit.coin.coin_pub), TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit.merchant_pub), TALER_MINT_PARSE_FIXED ("H_a", &deposit.h_contract), @@ -146,32 +146,9 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, return MHD_NO; /* hard failure */ if (GNUNET_NO == res) return MHD_YES; /* failure */ - deposit.coin.denom_pub - = GNUNET_CRYPTO_rsa_public_key_decode (spec[0].destination, - spec[0].destination_size_out); - if (NULL == deposit.coin.denom_pub) - { - LOG_WARNING ("Failed to parse denomination key for /deposit request\n"); - TALER_MINT_release_parsed_data (spec); - return TALER_MINT_reply_arg_invalid (connection, - "denom_pub"); - } - deposit.coin.denom_sig - = GNUNET_CRYPTO_rsa_signature_decode (spec[1].destination, - spec[1].destination_size_out); - if (NULL == deposit.coin.denom_sig) - { - LOG_WARNING ("Failed to parse unblinded signature for /deposit request\n"); - GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); - TALER_MINT_release_parsed_data (spec); - return TALER_MINT_reply_arg_invalid (connection, - "denom_pub"); - } /* FIXME: check that "wire" is formatted correctly */ if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS))) { - GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); - GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig); LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n"); TALER_MINT_release_parsed_data (spec); return TALER_MINT_reply_arg_invalid (connection, @@ -187,8 +164,6 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, deposit.amount = *amount; res = verify_and_execute_deposit (connection, &deposit); - GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); - GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig); TALER_MINT_release_parsed_data (spec); return res; } diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c index 165654193..9c732ce6e 100644 --- a/src/mint/taler-mint-httpd_parsing.c +++ b/src/mint/taler-mint-httpd_parsing.c @@ -260,402 +260,6 @@ TALER_MINT_parse_post_cleanup_callback (void *con_cls) } -/** - * Navigate through a JSON tree. - * - * Sends an error response if navigation is impossible (i.e. - * the JSON object is invalid) - * - * @param connection the connection to send an error response to - * @param root the JSON node to start the navigation at. - * @param ... navigation specification (see `enum TALER_MINT_JsonNavigationCommand`) - * @return - * #GNUNET_YES if navigation was successful - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error (no response was generated, - * connection must be closed) - */ -int -GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, - const json_t *root, - ...) -{ - va_list argp; - int ret; - json_t *path; /* what's our current path from 'root'? */ - - path = json_array (); - va_start (argp, root); - ret = 2; /* just not any of the valid return values */ - while (2 == ret) - { - enum TALER_MINT_JsonNavigationCommand command - = va_arg (argp, - enum TALER_MINT_JsonNavigationCommand); - - switch (command) - { - case JNAV_FIELD: - { - const char *fname = va_arg(argp, const char *); - - json_array_append_new (path, - json_string (fname)); - root = json_object_get (root, - fname); - if (NULL == root) - { - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:s, s:o}", - "error", - "missing field in JSON", - "field", - fname, - "path", - path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - break; - case JNAV_INDEX: - { - int fnum = va_arg(argp, int); - - json_array_append_new (path, - json_integer (fnum)); - root = json_array_get (root, - fnum); - if (NULL == root) - { - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:o}", - "error", - "missing index in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - break; - case JNAV_RET_DATA: - { - void *where = va_arg (argp, void *); - size_t len = va_arg (argp, size_t); - const char *str; - int res; - - str = json_string_value (root); - if (NULL == str) - { - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:o}", - "error", - "string expected", - "path", - path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - res = GNUNET_STRINGS_string_to_data (str, strlen (str), - where, len); - if (GNUNET_OK != res) - { - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:o}", - "error", - "malformed binary data in JSON", - "path", - path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - ret = GNUNET_OK; - } - break; - case JNAV_RET_DATA_VAR: - { - void **where = va_arg (argp, void **); - size_t *len = va_arg (argp, size_t *); - const char *str; - - str = json_string_value (root); - if (NULL == str) - { - ret = (MHD_YES == - TALER_MINT_reply_internal_error (connection, - "json_string_value() failed")) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - *len = (strlen (str) * 5) / 8; - if (NULL != where) - { - int res; - - *where = GNUNET_malloc (*len); - res = GNUNET_STRINGS_string_to_data (str, - strlen (str), - *where, - *len); - if (GNUNET_OK != res) - { - GNUNET_free (*where); - *where = NULL; - *len = 0; - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:o}", - "error", - "malformed binary data in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - ret = GNUNET_OK; - } - break; - case JNAV_RET_TYPED_JSON: - { - int typ = va_arg (argp, int); - const json_t **r_json = va_arg (argp, const json_t **); - - if ( (-1 != typ) && (json_typeof (root) != typ)) - { - ret = (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:i, s:i, s:o}", - "error", "wrong JSON field type", - "type_expected", typ, - "type_actual", json_typeof (root), - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - *r_json = root; - ret = GNUNET_OK; - } - break; - default: - GNUNET_break (0); - ret = (MHD_YES == - TALER_MINT_reply_internal_error (connection, - "unhandled value in switch")) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - va_end (argp); - json_decref (path); - return ret; -} - - -/** - * Parse JSON object into components based on the given field - * specification. - * - * @param connection the connection to send an error response to - * @param root the JSON node to start the navigation at. - * @param spec field specification for the parser - * @return - * #GNUNET_YES if navigation was successful (caller is responsible - * for freeing allocated variable-size data using - * #TALER_MINT_release_parsed_data() when done) - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_parse_json_data (struct MHD_Connection *connection, - const json_t *root, - struct GNUNET_MINT_ParseFieldSpec *spec) -{ - unsigned int i; - int ret; - void *ptr; - - ret = GNUNET_YES; - for (i=0; NULL != spec[i].field_name; i++) - { - switch (spec[i].command) - { - case JNAV_FIELD: - GNUNET_break (0); - return GNUNET_SYSERR; - case JNAV_INDEX: - GNUNET_break (0); - return GNUNET_SYSERR; - case JNAV_RET_DATA: - if (GNUNET_YES != ret) - break; - ret = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - spec[i].field_name, - JNAV_RET_DATA, - spec[i].destination, - spec[i].destination_size_in); - break; - case JNAV_RET_DATA_VAR: - if (GNUNET_YES != ret) - break; - ptr = NULL; - ret = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - spec[i].field_name, - JNAV_RET_DATA_VAR, - &ptr, - &spec[i].destination_size_out); - spec[i].destination = ptr; - break; - case JNAV_RET_TYPED_JSON: - if (GNUNET_YES != ret) - break; - ptr = NULL; - ret = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - spec[i].field_name, - JNAV_RET_TYPED_JSON, - spec[i].type, - &ptr); - spec[i].destination = ptr; - break; - } - } - if (GNUNET_YES != ret) - TALER_MINT_release_parsed_data (spec); - return ret; -} - - -/** - * Release all memory allocated for the variable-size fields in - * the parser specification. - * - * @param spec specification to free - */ -void -TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec) -{ - unsigned int i; - - for (i=0; NULL != spec[i].field_name; i++) - { - switch (spec[i].command) - { - case JNAV_FIELD: - GNUNET_break (0); - return; - case JNAV_INDEX: - GNUNET_break (0); - return; - case JNAV_RET_DATA: - break; - case JNAV_RET_DATA_VAR: - if (0 != spec[i].destination_size_out) - { - GNUNET_free (spec[i].destination); - spec[i].destination = NULL; - spec[i].destination_size_out = 0; - } - break; - case JNAV_RET_TYPED_JSON: - if (NULL != spec[i].destination) - { - json_decref (spec[i].destination); - spec[i].destination = NULL; - } - break; - } - } -} - - -/** - * Parse amount specified in JSON format. - * - * @param connection the MHD connection (to report errors) - * @param f json specification of the amount - * @param amount[OUT] set to the amount specified in @a f - * @return - * #GNUNET_YES if parsing was successful - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error, error response was not generated - */ -int -TALER_MINT_parse_amount_json (struct MHD_Connection *connection, - json_t *f, - struct TALER_Amount *amount) -{ - json_int_t value; - json_int_t fraction; - const char *currency; - struct TALER_Amount a; - - if (-1 == json_unpack (f, - "{s:I, s:I, s:s}", - "value", &value, - "fraction", &fraction, - "currency", ¤cy)) - { - LOG_WARNING ("Failed to parse JSON amount specification\n"); - if (MHD_YES != - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "Bad format")) - return GNUNET_SYSERR; - return GNUNET_NO; - } - if ( (value < 0) || - (fraction < 0) || - (value > UINT32_MAX) || - (fraction > UINT32_MAX) ) - { - LOG_WARNING ("Amount specified not in allowed range\n"); - if (MHD_YES != - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "Amount outside of allowed range")) - return GNUNET_SYSERR; - return GNUNET_NO; - } - if (0 != strcmp (currency, - MINT_CURRENCY)) - { - LOG_WARNING ("Currency specified not supported by this mint\n"); - if (MHD_YES != - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:s}", - "error", "Currency not supported", - "currency", currency)) - return GNUNET_SYSERR; - return GNUNET_NO; - } - a.value = (uint32_t) value; - a.fraction = (uint32_t) fraction; - GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN); - strcpy (a.currency, MINT_CURRENCY); - *amount = TALER_amount_normalize (a); - return GNUNET_OK; -} - - /** * Extract base32crockford encoded data from request. * @@ -756,5 +360,576 @@ TALER_MINT_mhd_request_var_arg_data (struct MHD_Connection *connection, } +/** + * Navigate through a JSON tree. + * + * Sends an error response if navigation is impossible (i.e. + * the JSON object is invalid) + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param ... navigation specification (see `enum TALER_MINT_JsonNavigationCommand`) + * @return + * #GNUNET_YES if navigation was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error (no response was generated, + * connection must be closed) + */ +int +GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, + const json_t *root, + ...) +{ + va_list argp; + int ret; + json_t *path; /* what's our current path from 'root'? */ + + path = json_array (); + va_start (argp, root); + ret = 2; /* just not any of the valid return values */ + while (2 == ret) + { + enum TALER_MINT_JsonNavigationCommand command + = va_arg (argp, + enum TALER_MINT_JsonNavigationCommand); + + switch (command) + { + case JNAV_FIELD: + { + const char *fname = va_arg(argp, const char *); + + json_array_append_new (path, + json_string (fname)); + root = json_object_get (root, + fname); + if (NULL == root) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:o}", + "error", + "missing field in JSON", + "field", + fname, + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + break; + + case JNAV_INDEX: + { + int fnum = va_arg(argp, int); + + json_array_append_new (path, + json_integer (fnum)); + root = json_array_get (root, + fnum); + if (NULL == root) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "missing index in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + break; + + case JNAV_RET_DATA: + { + void *where = va_arg (argp, void *); + size_t len = va_arg (argp, size_t); + const char *str; + int res; + + // FIXME: avoidable code duplication here... + str = json_string_value (root); + if (NULL == str) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "string expected", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + where, len); + if (GNUNET_OK != res) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed binary data in JSON", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + } + break; + + case JNAV_RET_DATA_VAR: + { + void **where = va_arg (argp, void **); + size_t *len = va_arg (argp, size_t *); + const char *str; + + // FIXME: avoidable code duplication here... + str = json_string_value (root); + if (NULL == str) + { + ret = (MHD_YES == + TALER_MINT_reply_internal_error (connection, + "json_string_value() failed")) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *len = (strlen (str) * 5) / 8; + if (NULL != where) + { + int res; + + *where = GNUNET_malloc (*len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + *where, + *len); + if (GNUNET_OK != res) + { + GNUNET_free (*where); + *where = NULL; + *len = 0; + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed binary data in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + ret = GNUNET_OK; + } + break; + + case JNAV_RET_TYPED_JSON: + { + int typ = va_arg (argp, int); + const json_t **r_json = va_arg (argp, const json_t **); + + if ( (-1 != typ) && (json_typeof (root) != typ)) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:i, s:i, s:o}", + "error", "wrong JSON field type", + "type_expected", typ, + "type_actual", json_typeof (root), + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *r_json = root; + ret = GNUNET_OK; + } + break; + + case JNAV_RET_RSA_PUBLIC_KEY: + { + void **where = va_arg (argp, void **); + size_t len; + const char *str; + int res; + void *buf; + + // FIXME: avoidable code duplication here... + str = json_string_value (root); + if (NULL == str) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "string expected", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + len = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + len); + if (GNUNET_OK != res) + { + GNUNET_free (buf); + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed binary data in JSON", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *where = GNUNET_CRYPTO_rsa_public_key_decode (buf, + len); + GNUNET_free (buf); + if (NULL == *where) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed RSA public key in JSON", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + break; + } + + case JNAV_RET_RSA_SIGNATURE: + { + void **where = va_arg (argp, void **); + size_t len; + const char *str; + int res; + void *buf; + + // FIXME: avoidable code duplication here... + str = json_string_value (root); + if (NULL == str) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "string expected", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + len = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + len); + if (GNUNET_OK != res) + { + GNUNET_free (buf); + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed binary data in JSON", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *where = GNUNET_CRYPTO_rsa_signature_decode (buf, + len); + GNUNET_free (buf); + if (NULL == *where) + { + ret = (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed RSA signature in JSON", + "path", + path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + break; + } + + default: + GNUNET_break (0); + ret = (MHD_YES == + TALER_MINT_reply_internal_error (connection, + "unhandled value in switch")) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + va_end (argp); + json_decref (path); + return ret; +} + + +/** + * Parse JSON object into components based on the given field + * specification. + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param spec field specification for the parser + * @return + * #GNUNET_YES if navigation was successful (caller is responsible + * for freeing allocated variable-size data using + * #TALER_MINT_release_parsed_data() when done) + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +int +TALER_MINT_parse_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_MINT_ParseFieldSpec *spec) +{ + unsigned int i; + int ret; + void *ptr; + + ret = GNUNET_YES; + for (i=0; NULL != spec[i].field_name; i++) + { + switch (spec[i].command) + { + case JNAV_FIELD: + GNUNET_break (0); + return GNUNET_SYSERR; + case JNAV_INDEX: + GNUNET_break (0); + return GNUNET_SYSERR; + case JNAV_RET_DATA: + if (GNUNET_YES != ret) + break; + ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, + spec[i].field_name, + JNAV_RET_DATA, + spec[i].destination, + spec[i].destination_size_in); + break; + case JNAV_RET_DATA_VAR: + if (GNUNET_YES != ret) + break; + ptr = NULL; + ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, + spec[i].field_name, + JNAV_RET_DATA_VAR, + &ptr, + &spec[i].destination_size_out); + spec[i].destination = ptr; + break; + case JNAV_RET_TYPED_JSON: + if (GNUNET_YES != ret) + break; + ptr = NULL; + ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, + spec[i].field_name, + JNAV_RET_TYPED_JSON, + spec[i].type, + &ptr); + *((void**)spec[i].destination) = ptr; + break; + case JNAV_RET_RSA_PUBLIC_KEY: + if (GNUNET_YES != ret) + break; + ptr = NULL; + ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, + spec[i].field_name, + JNAV_RET_RSA_PUBLIC_KEY, + &ptr); + spec[i].destination = ptr; + break; + case JNAV_RET_RSA_SIGNATURE: + if (GNUNET_YES != ret) + break; + ptr = NULL; + ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, + spec[i].field_name, + JNAV_RET_RSA_SIGNATURE, + &ptr); + spec[i].destination = ptr; + break; + } + } + if (GNUNET_YES != ret) + TALER_MINT_release_parsed_data (spec); + return ret; +} + + +/** + * Release all memory allocated for the variable-size fields in + * the parser specification. + * + * @param spec specification to free + */ +void +TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec) +{ + unsigned int i; + void *ptr; + + for (i=0; NULL != spec[i].field_name; i++) + { + switch (spec[i].command) + { + case JNAV_FIELD: + GNUNET_break (0); + return; + case JNAV_INDEX: + GNUNET_break (0); + return; + case JNAV_RET_DATA: + break; + case JNAV_RET_DATA_VAR: + if (0 != spec[i].destination_size_out) + { + GNUNET_free (spec[i].destination); + spec[i].destination = NULL; + spec[i].destination_size_out = 0; + } + break; + case JNAV_RET_TYPED_JSON: + ptr = *(void **) spec[i].destination; + if (NULL != ptr) + { + json_decref (ptr); + *(void**) spec[i].destination = NULL; + } + break; + case JNAV_RET_RSA_PUBLIC_KEY: + ptr = *(void **) spec[i].destination; + if (NULL != ptr) + { + GNUNET_CRYPTO_rsa_public_key_free (ptr); + *(void**) spec[i].destination = NULL; + } + break; + case JNAV_RET_RSA_SIGNATURE: + ptr = *(void **) spec[i].destination; + if (NULL != ptr) + { + GNUNET_CRYPTO_rsa_signature_free (ptr); + *(void**) spec[i].destination = NULL; + } + break; + } + } +} + + +/** + * Parse amount specified in JSON format. + * + * @param connection the MHD connection (to report errors) + * @param f json specification of the amount + * @param amount[OUT] set to the amount specified in @a f + * @return + * #GNUNET_YES if parsing was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error, error response was not generated + */ +int +TALER_MINT_parse_amount_json (struct MHD_Connection *connection, + json_t *f, + struct TALER_Amount *amount) +{ + json_int_t value; + json_int_t fraction; + const char *currency; + struct TALER_Amount a; + + if (-1 == json_unpack (f, + "{s:I, s:I, s:s}", + "value", &value, + "fraction", &fraction, + "currency", ¤cy)) + { + LOG_WARNING ("Failed to parse JSON amount specification\n"); + if (MHD_YES != + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "Bad format")) + return GNUNET_SYSERR; + return GNUNET_NO; + } + if ( (value < 0) || + (fraction < 0) || + (value > UINT32_MAX) || + (fraction > UINT32_MAX) ) + { + LOG_WARNING ("Amount specified not in allowed range\n"); + if (MHD_YES != + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "Amount outside of allowed range")) + return GNUNET_SYSERR; + return GNUNET_NO; + } + if (0 != strcmp (currency, + MINT_CURRENCY)) + { + LOG_WARNING ("Currency specified not supported by this mint\n"); + if (MHD_YES != + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s}", + "error", "Currency not supported", + "currency", currency)) + return GNUNET_SYSERR; + return GNUNET_NO; + } + a.value = (uint32_t) value; + a.fraction = (uint32_t) fraction; + GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN); + strcpy (a.currency, MINT_CURRENCY); + *amount = TALER_amount_normalize (a); + return GNUNET_OK; +} + + /* end of taler-mint-httpd_parsing.c */ diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h index 86205a070..f14090172 100644 --- a/src/mint/taler-mint-httpd_parsing.h +++ b/src/mint/taler-mint-httpd_parsing.h @@ -108,7 +108,19 @@ enum TALER_MINT_JsonNavigationCommand * or -1 for any type). * Params: (int, json_t **) */ - JNAV_RET_TYPED_JSON + JNAV_RET_TYPED_JSON, + + /** + * Return a `struct GNUNET_CRYPTO_rsa_PublicKey` which was + * encoded as variable-size base32crockford encoded data. + */ + JNAV_RET_RSA_PUBLIC_KEY, + + /** + * Return a `struct GNUNET_CRYPTO_rsa_Signature` which was + * encoded as variable-size base32crockford encoded data. + */ + JNAV_RET_RSA_SIGNATURE }; @@ -230,15 +242,34 @@ TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec); * Generate line in parser specification for JSON array value. * * @param field name of the field + * @param ptraddr address of pointer to initialize (a `void **`) */ -#define TALER_MINT_PARSE_ARRAY(field) { field, NULL, 0, 0, JNAV_RET_TYPED_JSON, JSON_ARRAY } +#define TALER_MINT_PARSE_ARRAY(field,ptraddr) { field, ptraddr, 0, 0, JNAV_RET_TYPED_JSON, JSON_ARRAY } /** * Generate line in parser specification for JSON object value. * * @param field name of the field + * @param ptraddr address of pointer to initialize (a `void **`) */ -#define TALER_MINT_PARSE_OBJECT(field) { field, NULL, 0, 0, JNAV_RET_TYPED_JSON, JSON_OBJECT } +#define TALER_MINT_PARSE_OBJECT(field,ptraddr) { field, ptraddr, 0, 0, JNAV_RET_TYPED_JSON, JSON_OBJECT } + +/** + * Generate line in parser specification for RSA public key. + * + * @param field name of the field + * @param ptraddr address of `struct GNUNET_CRYPTO_rsa_PublicKey *` initialize + */ +#define TALER_MINT_PARSE_RSA_PUBLIC_KEY(field,ptrpk) { field, ptrpk, 0, 0, JNAV_RET_RSA_PUBLIC_KEY, 0 } + +/** + * Generate line in parser specification for RSA public key. + * + * @param field name of the field + * @param ptrsig address of `struct GNUNET_CRYPTO_rsa_Signature *` initialize + */ +#define TALER_MINT_PARSE_RSA_SIGNATURE(field,ptrsig) { field, ptrsig, 0, 0, JNAV_RET_RSA_SIGNATURE, 0 } + /** diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 89aff83ab..198ad423e 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -107,9 +107,9 @@ request_json_require_coin_public_info (struct MHD_Connection *connection, struct GNUNET_CRYPTO_rsa_PublicKey *pk; struct GNUNET_MINT_ParseFieldSpec spec[] = { - TALER_MINT_PARSE_FIXED("coin_pub", &r_public_info->coin_pub), - TALER_MINT_PARSE_VARIABLE("denom_sig"), - TALER_MINT_PARSE_VARIABLE("denom_pub"), + TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), + TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), + TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), TALER_MINT_PARSE_END }; @@ -118,21 +118,7 @@ request_json_require_coin_public_info (struct MHD_Connection *connection, spec); if (GNUNET_OK != ret) return ret; - sig = GNUNET_CRYPTO_rsa_signature_decode (spec[1].destination, - spec[1].destination_size_out); - pk = GNUNET_CRYPTO_rsa_public_key_decode (spec[2].destination, - spec[2].destination_size_out); - TALER_MINT_release_parsed_data (spec); - if ( (NULL == pk) || - (NULL == sig) ) - { - if (NULL != sig) - GNUNET_CRYPTO_rsa_signature_free (sig); - if (NULL != pk) - GNUNET_CRYPTO_rsa_public_key_free (pk); - // FIXME: send error reply... - return GNUNET_NO; - } + // TALER_MINT_release_parsed_data (spec); r_public_info->denom_sig = sig; r_public_info->denom_pub = pk; return GNUNET_OK; @@ -152,8 +138,8 @@ request_json_require_coin_public_info (struct MHD_Connection *connection, */ static int request_json_check_signature (struct MHD_Connection *connection, - json_t *root, - struct GNUNET_CRYPTO_EddsaPublicKey *pub, + const json_t *root, + const struct GNUNET_CRYPTO_EddsaPublicKey *pub, struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) { struct GNUNET_CRYPTO_EddsaSignature signature; @@ -230,100 +216,38 @@ request_json_check_signature (struct MHD_Connection *connection, /** - * Handle a "/refresh/melt" request + * Handle a "/refresh/melt" request after the first parsing has happened. + * We now need to validate the coins being melted and the session signature + * and then hand things of to execute the melt operation. * - * @param rh context of the handler * @param connection the MHD connection to handle - * @param[IN|OUT] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data + * @param refresh_session_pub public key of the melt operation + * @param new_denoms array of denomination keys + * @param melt_coins array of coins to melt + * @param melt_sig_json signature affirming the melt operation * @return MHD result code */ -int -TALER_MINT_handler_refresh_melt (struct RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) +static int +handle_refresh_melt_json (struct MHD_Connection *connection, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + const json_t *new_denoms, + const json_t *melt_coins, + const json_t *melt_sig_json) { - json_t *root; - struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub; int res; - json_t *new_denoms; unsigned int num_new_denoms; unsigned int i; struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; - json_t *melt_coins; struct TALER_CoinPublicInfo *coin_public_infos; unsigned int coin_count; struct GNUNET_HashContext *hash_context; struct GNUNET_HashCode melt_hash; struct MintKeyState *key_state; struct RefreshMeltSignatureBody body; - json_t *melt_sig_json; char *buf; size_t buf_size; struct TALER_MINT_DenomKeyIssuePriv *dki; - res = TALER_MINT_parse_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - return MHD_NO; - if ( (GNUNET_NO == res) || (NULL == root) ) - return MHD_YES; - - res = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - "session_pub", - JNAV_RET_DATA, - &refresh_session_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - if (GNUNET_SYSERR == res) - return MHD_NO; - if (GNUNET_NO == res) - return MHD_YES; - res = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - "new_denoms", - JNAV_RET_TYPED_JSON, - JSON_ARRAY, - &new_denoms); - if (GNUNET_SYSERR == res) - return MHD_NO; - if (GNUNET_NO == res) - return MHD_YES; - - res = GNUNET_MINT_parse_navigate_json (connection, - root, - JNAV_FIELD, - "melt_coins", - JNAV_RET_TYPED_JSON, - JSON_ARRAY, - &melt_coins); - if (GNUNET_OK != res) - { - // FIXME: leaks! - return res; - } - - melt_sig_json = json_object_get (root, - "melt_signature"); - if (NULL == melt_sig_json) - { - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", - "melt_signature missing"); - } - - - num_new_denoms = json_array_size (new_denoms); denom_pubs = GNUNET_malloc (num_new_denoms * @@ -331,8 +255,7 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh, for (i=0;i